1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2020 Olof Hagsand
6 Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
7
8 This file is part of CLIXON.
9
10 Licensed under the Apache License, Version 2.0 (the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21
22 Alternatively, the contents of this file may be used under the terms of
23 the GNU General Public License Version 3 or later (the "GPL"),
24 in which case the provisions of the GPL are applicable instead
25 of those above. If you wish to allow use of your version of this file only
26 under the terms of the GPL, and not to allow others to
27 use your version of this file under the terms of Apache License version 2,
28 indicate your decision by deleting the provisions above and replace them with
29 the notice and other provisions required by the GPL. If you do not delete
30 the provisions above, a recipient may use your version of this file under
31 the terms of any one of the Apache License version 2 or the GPL.
32
33 ***** END LICENSE BLOCK *****
34 *
35 * Return errors
36 * @see RFC 7231 Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
37 */
38
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <signal.h>
44 #include <syslog.h>
45 #include <fcntl.h>
46 #include <ctype.h>
47 #include <time.h>
48 #include <signal.h>
49 #include <dlfcn.h>
50 #include <sys/param.h>
51 #include <sys/time.h>
52 #include <sys/wait.h>
53
54 /* cligen */
55 #include <cligen/cligen.h>
56
57 /* clicon */
58 #include <clixon/clixon.h>
59
60 #include "restconf_lib.h"
61 #include "restconf_api.h"
62 #include "restconf_err.h"
63
64 /*
65 * Constants
66 */
67 /* In the fcgi implementations some errors had body, it would be cleaner to skip them
68 * None seem mandatory according to RFC 7231
69 */
70 #define SKIP_BODY
71
72 /*
73 * NOTE, fcgi seems not enough with a status code (libevhtp is) but must also have a status
74 * header.
75 */
76
77 /*! HTTP error 400
78 * @param[in] h Clicon handle
79 * @param[in] req Generic Www handle
80 */
81 int
restconf_badrequest(clicon_handle h,void * req)82 restconf_badrequest(clicon_handle h,
83 void *req)
84 {
85 int retval = -1;
86
87 #ifdef SKIP_BODY /* Remove the body - should it really be there? */
88 if (restconf_reply_send(req, 400, NULL) < 0)
89 goto done;
90 retval = 0;
91 done:
92 #else
93 char *path;
94 cbuf *cb = NULL;
95
96 /* Create body */
97 if ((cb = cbuf_new()) == NULL){
98 clicon_err(OE_UNIX, errno, "cbuf_new");
99 goto done;
100 }
101 path = restconf_param_get("REQUEST_URI", r->envp);
102 if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
103 goto done;
104 cprintf(cb, "The requested URL %s or data is in some way badly formed.\n", path);
105 if (restconf_reply_send(req, 400, cb) < 0)
106 goto done;
107 retval = 0;
108 done:
109 if (cb)
110 cbuf_free(cb);
111 #endif
112 return retval;
113 }
114
115 /*! HTTP error 401
116 * @param[in] h Clicon handle
117 * @param[in] req Generic Www handle
118 */
119 int
restconf_unauthorized(clicon_handle h,void * req)120 restconf_unauthorized(clicon_handle h,
121 void *req)
122
123 {
124 int retval = -1;
125
126 #ifdef SKIP_BODY /* Remove the body - should it really be there? */
127 if (restconf_reply_send(req, 400, NULL) < 0)
128 goto done;
129 retval = 0;
130 done:
131 #else
132 char *path;
133 cbuf *cb = NULL;
134
135 /* Create body */
136 if ((cb = cbuf_new()) == NULL){
137 clicon_err(OE_UNIX, errno, "cbuf_new");
138 goto done;
139 }
140 path = restconf_param_get("REQUEST_URI", r->envp);
141 if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
142 goto done;
143 cprintf(cb, "<error-tag>access-denied</error-tag>\n");
144 cprintf(cb, "The requested URL %s was unauthorized.\n", path);
145 if (restconf_reply_send(req, 400, cb) < 0)
146 goto done;
147 retval = 0;
148 done:
149 if (cb)
150 cbuf_free(cb);
151 #endif
152 return retval;
153 }
154
155 /*! HTTP error 403
156 * @param[in] h Clicon handle
157 * @param[in] req Generic Www handle
158 */
159 int
restconf_forbidden(clicon_handle h,void * req)160 restconf_forbidden(clicon_handle h,
161 void *req)
162 {
163 int retval = -1;
164 #ifdef SKIP_BODY /* Remove the body - should it really be there? */
165 if (restconf_reply_send(req, 403, NULL) < 0)
166 goto done;
167 retval = 0;
168 done:
169 #else
170 char *path;
171 cbuf *cb = NULL;
172
173 /* Create body */
174 if ((cb = cbuf_new()) == NULL){
175 clicon_err(OE_UNIX, errno, "cbuf_new");
176 goto done;
177 }
178 path = restconf_param_get("REQUEST_URI", r->envp);
179 if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
180 goto done;
181 cprintf(cb, "The requested URL %s was forbidden.\n", path);
182 if (restconf_reply_send(req, 403, cb) < 0)
183 goto done;
184 retval = 0;
185 done:
186 if (cb)
187 cbuf_free(cb);
188 #endif
189 return retval;
190 }
191
192 /*! HTTP error 404
193 * @param[in] h Clicon handle
194 * @param[in] req Generic Www handle
195 * XXX skip body?
196 */
197 int
restconf_notfound(clicon_handle h,void * req)198 restconf_notfound(clicon_handle h,
199 void *req)
200 {
201 int retval = -1;
202 #ifdef SKIP_BODY /* Remove the body - should it really be there? */
203 if (restconf_reply_send(req, 404, NULL) < 0)
204 goto done;
205 retval = 0;
206 done:
207 #else
208 char *path;
209 cbuf *cb = NULL;
210
211 /* Create body */
212 if ((cb = cbuf_new()) == NULL){
213 clicon_err(OE_UNIX, errno, "cbuf_new");
214 goto done;
215 }
216 path = restconf_param_get("REQUEST_URI", r->envp);
217 if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
218 goto done;
219 cprintf(cb, "The requested URL %s was not found on this server.\n", path);
220 if (restconf_reply_send(req, 404, cb) < 0)
221 goto done;
222 retval = 0;
223 done:
224 if (cb)
225 cbuf_free(cb);
226 #endif
227 return retval;
228 }
229
230 /*! HTTP error 405
231 * @param[in] req Generic Www handle
232 * @param[in] allow Which methods are allowed
233 */
234 int
restconf_method_notallowed(void * req,char * allow)235 restconf_method_notallowed(void *req,
236 char *allow)
237 {
238 int retval = -1;
239
240 if (restconf_reply_header(req, "Allow", "%s", allow) < 0)
241 goto done;
242 if (restconf_reply_send(req, 405, NULL) < 0)
243 goto done;
244 retval = 0;
245 done:
246 return retval;
247 }
248
249 /*! HTTP error 406 Not acceptable
250 * @param[in] h Clicon handle
251 * @param[in] req Generic Www handle
252 */
253 int
restconf_notacceptable(clicon_handle h,void * req)254 restconf_notacceptable(clicon_handle h,
255 void *req)
256 {
257 int retval = -1;
258
259 #ifdef SKIP_BODY /* Remove the body - should it really be there? */
260 if (restconf_reply_send(req, 406, NULL) < 0)
261 goto done;
262 retval = 0;
263 done:
264 #else
265 char *path;
266 cbuf *cb = NULL;
267
268 /* Create body */
269 if ((cb = cbuf_new()) == NULL){
270 clicon_err(OE_UNIX, errno, "cbuf_new");
271 goto done;
272 }
273 path = restconf_param_get("REQUEST_URI", r->envp);
274 if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
275 goto done;
276 cprintf(cb, "The target resource does not have a current representation that would be acceptable to the user agent.\n", path);
277 if (restconf_reply_send(req, 406, cb) < 0)
278 goto done;
279 retval = 0;
280 done:
281 if (cb)
282 cbuf_free(cb);
283 #endif
284 return retval;
285 }
286
287 /*! HTTP error 409
288 * @param[in] req Generic Www handle
289 */
290 int
restconf_conflict(void * req)291 restconf_conflict(void *req)
292
293 {
294 int retval = -1;
295
296 if (restconf_reply_send(req, 409, NULL) < 0)
297 goto done;
298 retval = 0;
299 done:
300 return retval;
301 }
302
303 /*! HTTP error 409 Unsupporte dmedia
304 * @param[in] req Generic Www handle
305 */
306 int
restconf_unsupported_media(void * req)307 restconf_unsupported_media(void *req)
308 {
309 int retval = -1;
310
311 if (restconf_reply_send(req, 415, NULL) < 0)
312 goto done;
313 retval = 0;
314 done:
315 return retval;
316 }
317
318 /*! HTTP error 500 Internal server error
319 * @param[in] h Clicon handle
320 * @param[in] req Generic Www handle
321 */
322 int
restconf_internal_server_error(clicon_handle h,void * req)323 restconf_internal_server_error(clicon_handle h,
324 void *req)
325 {
326 int retval = -1;
327 #ifdef SKIP_BODY /* Remove the body - should it really be there? */
328 if (restconf_reply_send(req, 500, NULL) < 0)
329 goto done;
330 retval = 0;
331 done:
332 #else
333 char *path;
334 cbuf *cb = NULL;
335
336 /* Create body */
337 if ((cb = cbuf_new()) == NULL){
338 clicon_err(OE_UNIX, errno, "cbuf_new");
339 goto done;
340 }
341 path = restconf_param_get("REQUEST_URI", r->envp);
342 if (restconf_reply_header(req, "Content-Type", "text/html") < 0)
343 goto done;
344 cprintf(cb, "Internal server error when accessing %s</h1>\n", path);
345 if (restconf_reply_send(req, 500, cb) < 0)
346 goto done;
347 retval = 0;
348 done:
349 if (cb)
350 cbuf_free(cb);
351 #endif
352 return retval;
353 }
354
355 /*! HTTP error 501 Not implemented
356 * @param[in] req Generic Www handle
357 */
358 int
restconf_notimplemented(void * req)359 restconf_notimplemented(void *req)
360 {
361 int retval = -1;
362
363 if (restconf_reply_send(req, 501, NULL) < 0)
364 goto done;
365 retval = 0;
366 done:
367 return retval;
368 }
369
370 /*! Generic restconf error function on get/head request
371 * @param[in] h Clixon handle
372 * @param[in] req Generic Www handle
373 * @param[in] xerr XML error message from backend
374 * @param[in] pretty Set to 1 for pretty-printed xml/json output
375 * @param[in] media Output media
376 * @param[in] code If 0 use rfc8040 sec 7 netconf2restconf error-tag mapping
377 * otherwise use this code
378 */
379 int
api_return_err(clicon_handle h,void * req,cxobj * xerr,int pretty,restconf_media media,int code0)380 api_return_err(clicon_handle h,
381 void *req,
382 cxobj *xerr,
383 int pretty,
384 restconf_media media,
385 int code0)
386 {
387 int retval = -1;
388 cbuf *cb = NULL;
389 cbuf *cberr = NULL;
390 cxobj *xtag;
391 char *tagstr;
392 int code;
393 cxobj *xerr2 = NULL;
394
395 clicon_debug(1, "%s", __FUNCTION__);
396 if ((cb = cbuf_new()) == NULL){
397 clicon_err(OE_UNIX, errno, "cbuf_new");
398 goto done;
399 }
400 /* A well-formed error message when entering here should look like:
401 * <rpc-error>...<error-tag>invalid-value</error-tag>
402 * Check this is so, otherwise generate an internal error.
403 */
404 if (strcmp(xml_name(xerr), "rpc-error") != 0 ||
405 (xtag = xpath_first(xerr, NULL, "error-tag")) == NULL){
406 if ((cberr = cbuf_new()) == NULL){
407 clicon_err(OE_UNIX, errno, "cbuf_new");
408 goto done;
409 }
410 cprintf(cberr, "Internal error, system returned invalid error message: ");
411 if (netconf_err2cb(xerr, cberr) < 0)
412 goto done;
413 if (netconf_operation_failed_xml(&xerr2, "application",
414 cbuf_get(cberr)) < 0)
415 goto done;
416 if ((xerr = xpath_first(xerr2, NULL, "//rpc-error")) == NULL){
417 clicon_err(OE_XML, 0, "Internal error, shouldnt happen");
418 goto done;
419 }
420 }
421 if (xml_name_set(xerr, "error") < 0)
422 goto done;
423 tagstr = xml_body(xtag);
424 if (code0 != 0)
425 code = code0;
426 else{
427 if ((code = restconf_err2code(tagstr)) < 0)
428 code = 500; /* internal server error */
429 }
430 if (restconf_reply_header(req, "Content_Type", "%s", restconf_media_int2str(media)) < 0)
431 goto done;
432 switch (media){
433 case YANG_DATA_XML:
434 clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
435 if (pretty){
436 cprintf(cb, " <errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">\n");
437 if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1) < 0)
438 goto done;
439 cprintf(cb, " </errors>\r\n");
440 }
441 else {
442 cprintf(cb, "<errors xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\">");
443 if (clicon_xml2cbuf(cb, xerr, 2, pretty, -1) < 0)
444 goto done;
445 cprintf(cb, "</errors>\r\n");
446 }
447 break;
448 case YANG_DATA_JSON:
449 clicon_debug(1, "%s code:%d err:%s", __FUNCTION__, code, cbuf_get(cb));
450 if (pretty){
451 cprintf(cb, "{\n\"ietf-restconf:errors\" : ");
452 if (xml2json_cbuf(cb, xerr, pretty) < 0)
453 goto done;
454 cprintf(cb, "\n}\r\n");
455 }
456 else{
457 cprintf(cb, "{");
458 cprintf(cb, "\"ietf-restconf:errors\":");
459 if (xml2json_cbuf(cb, xerr, pretty) < 0)
460 goto done;
461 cprintf(cb, "}\r\n");
462 }
463 break;
464 default:
465 clicon_err(OE_YANG, EINVAL, "Invalid media type %d", media);
466 goto done;
467 break;
468 } /* switch media */
469 if (restconf_reply_send(req, code, cb) < 0)
470 goto done;
471 // ok:
472 retval = 0;
473 done:
474 clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
475 if (cb)
476 cbuf_free(cb);
477 if (cberr)
478 cbuf_free(cberr);
479 return retval;
480 }
481