From 55010e7541c7d4ea28b01b1ffcc3a1b6102dcb87 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 9 Feb 2018 12:18:39 +0700 Subject: [PATCH] restconf credentials plugin --- apps/backend/backend_client.c | 2 +- apps/restconf/restconf_lib.c | 132 ++++++++++++++++++++++++------- apps/restconf/restconf_lib.h | 9 ++- apps/restconf/restconf_main.c | 32 +++++--- apps/restconf/restconf_methods.c | 9 +++ lib/src/clixon_xml_map.c | 5 +- 6 files changed, 141 insertions(+), 48 deletions(-) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 0fd9fad6..97b0ae79 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -1095,8 +1095,8 @@ from_client(int s, goto done; retval = 0; done: + clicon_debug(1, "%s retval=%d", __FUNCTION__, retval); if (msg) free(msg); - clicon_debug(1, "%s retval=%d", __FUNCTION__, retval); return retval; /* -1 here terminates backend */ } diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index c8b49512..15c868ca 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -148,7 +148,60 @@ restconf_code2reason(int code) return clicon_int2str(http_reason_phrase_map, code); } -/*! +/*! HTTP error 400 + * @param[in] r Fastcgi request handle + */ +int +badrequest(FCGX_Request *r) +{ + char *path; + + clicon_debug(1, "%s", __FUNCTION__); + path = FCGX_GetParam("DOCUMENT_URI", r->envp); + FCGX_FPrintF(r->out, "Status: 400\r\n"); /* 400 bad request */ + FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); + FCGX_FPrintF(r->out, "

Clixon Bad request/h1>\n"); + FCGX_FPrintF(r->out, "The requested URL %s or data is in some way badly formed.\n", + path); + return 0; +} + +/*! HTTP error 401 + * @param[in] r Fastcgi request handle + */ +int +unauthorized(FCGX_Request *r) +{ + char *path; + + clicon_debug(1, "%s", __FUNCTION__); + path = FCGX_GetParam("DOCUMENT_URI", r->envp); + FCGX_FPrintF(r->out, "Status: 401\r\n"); /* 401 unauthorized */ + FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); + FCGX_FPrintF(r->out, "access-denied\n"); + FCGX_FPrintF(r->out, "The requested URL %s was unauthorized.\n", path); + return 0; +} + +/*! HTTP error 403 + * @param[in] r Fastcgi request handle + */ +int +forbidden(FCGX_Request *r) +{ + char *path; + + clicon_debug(1, "%s", __FUNCTION__); + path = FCGX_GetParam("DOCUMENT_URI", r->envp); + FCGX_FPrintF(r->out, "Status: 403\r\n"); /* 403 forbidden */ + FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); + FCGX_FPrintF(r->out, "

Grideye Forbidden

\n"); + FCGX_FPrintF(r->out, "The requested URL %s was forbidden.\n", path); + return 0; +} + +/*! HTTP error 404 + * @param[in] r Fastcgi request handle */ int notfound(FCGX_Request *r) @@ -165,31 +218,9 @@ notfound(FCGX_Request *r) return 0; } -int -badrequest(FCGX_Request *r) -{ - char *path; - - clicon_debug(1, "%s", __FUNCTION__); - path = FCGX_GetParam("DOCUMENT_URI", r->envp); - FCGX_FPrintF(r->out, "Status: 400\r\n"); /* 400 bad request */ - FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); - FCGX_FPrintF(r->out, "

Clixon Bad request/h1>\n"); - FCGX_FPrintF(r->out, "The requested URL %s or data is in some way badly formed.\n", - path); - return 0; -} - -int -notimplemented(FCGX_Request *r) -{ - clicon_debug(1, "%s", __FUNCTION__); - FCGX_FPrintF(r->out, "Status: 501\r\n"); - FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); - FCGX_FPrintF(r->out, "

Not Implemented/h1>\n"); - return 0; -} - +/*! HTTP error 409 + * @param[in] r Fastcgi request handle + */ int conflict(FCGX_Request *r) { @@ -200,6 +231,35 @@ conflict(FCGX_Request *r) return 0; } +/*! HTTP error 500 + * @param[in] r Fastcgi request handle + */ +int +internal_server_error(FCGX_Request *r) +{ + char *path; + + clicon_debug(1, "%s", __FUNCTION__); + path = FCGX_GetParam("DOCUMENT_URI", r->envp); + FCGX_FPrintF(r->out, "Status: 500\r\n"); /* 500 internal server error */ + FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); + FCGX_FPrintF(r->out, "

Grideye Internal server error when accessing %s

\n", path); + return 0; +} + +/*! HTTP error 501 + * @param[in] r Fastcgi request handle + */ +int +notimplemented(FCGX_Request *r) +{ + clicon_debug(1, "%s", __FUNCTION__); + FCGX_FPrintF(r->out, "Status: 501\r\n"); + FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); + FCGX_FPrintF(r->out, "

Not Implemented/h1>\n"); + return 0; +} + /*! Specialization of clicon_debug with xml tree */ int clicon_debug_xml(int dbglevel, @@ -329,7 +389,10 @@ restconf_plugin_load(clicon_handle h) (int)strlen(filename), filename); if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL) goto quit; - _credentials_fn = dlsym(handle, PLUGIN_CREDENTIALS); + if ((_credentials_fn = dlsym(handle, PLUGIN_CREDENTIALS)) == NULL) + clicon_debug(1, "Failed to load %s", PLUGIN_CREDENTIALS); + else + clicon_debug(1, "%s callback loaded", PLUGIN_CREDENTIALS); if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) { clicon_err(OE_UNIX, errno, "realloc"); goto quit; @@ -383,6 +446,14 @@ restconf_plugin_start(clicon_handle h, return 0; } +/*! Run the restconf user-defined credentials callback if present + * The callback is expected to return the authenticated user, or NULL if not + * authenticasted. + * If no callback exists, return user "none" + * @param[in] h Clicon handle + * @param[in] r Fastcgi request handle + * @param[out] user The authenticated user (or NULL). Malloced, must be freed. + */ int restconf_credentials(clicon_handle h, FCGX_Request *r, @@ -397,13 +468,14 @@ restconf_credentials(clicon_handle h, clicon_err(OE_XML, errno, "strdup"); goto done; } - retval = 0; - goto done; + goto ok; } if (_credentials_fn(h, r, user) < 0) - user = NULL; + *user = NULL; + ok: retval = 0; done: + clicon_debug(1, "%s retval:%d user:%s", __FUNCTION__, retval, *user); return retval; } diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h index 271207ff..59e381f4 100644 --- a/apps/restconf/restconf_lib.h +++ b/apps/restconf/restconf_lib.h @@ -45,10 +45,15 @@ */ int restconf_err2code(char *tag); const char *restconf_code2reason(int code); -int notfound(FCGX_Request *r); + int badrequest(FCGX_Request *r); -int notimplemented(FCGX_Request *r); +int unauthorized(FCGX_Request *r); +int forbidden(FCGX_Request *r); +int notfound(FCGX_Request *r); int conflict(FCGX_Request *r); +int internal_server_error(FCGX_Request *r); +int notimplemented(FCGX_Request *r); + int clicon_debug_xml(int dbglevel, char *str, cxobj *cx); int test(FCGX_Request *r, int dbg); cbuf *readdata(FCGX_Request *r); diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index b0216204..54b422ed 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -317,27 +317,37 @@ api_restconf(clicon_handle h, if (str2cvec(data, '&', '=', &dvec) < 0) goto done; - retval = 0; test(r, 1); /* If present, check credentials. See "plugin_credentials" in plugin * See RFC 8040 section 2.5 */ if (restconf_credentials(h, r, &username) < 0) goto done; + clicon_debug(1, "%s username:%s", __FUNCTION__, username); clicon_debug(1, "%s credentials ok username:%s (should be non-NULL)", __FUNCTION__, username); - if (username == NULL) - goto done; - if (strcmp(method, "yang-library-version")==0) - retval = api_yang_library_version(h, r); - else if (strcmp(method, "data") == 0) /* restconf, skip /api/data */ - retval = api_data(h, r, path, pcvec, 2, qvec, data); - else if (strcmp(method, "operations") == 0) /* rpc */ - retval = api_operations(h, r, path, pcvec, 2, qvec, data, username); + if (username == NULL){ + unauthorized(r); + goto ok; + } + if (strcmp(method, "yang-library-version")==0){ + if (api_yang_library_version(h, r) < 0) + goto done; + } + else if (strcmp(method, "data") == 0){ /* restconf, skip /api/data */ + if (api_data(h, r, path, pcvec, 2, qvec, data) < 0) + goto done; + } + else if (strcmp(method, "operations") == 0){ /* rpc */ + if (api_operations(h, r, path, pcvec, 2, qvec, data, username) < 0) + goto done; + } else if (strcmp(method, "test") == 0) - retval = test(r, 0); + test(r, 0); else - retval = notfound(r); + notfound(r); + ok: + retval = 0; done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); if (pvec) diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 0248ee7b..6be25d41 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -906,8 +906,17 @@ api_operation_post(clicon_handle h, if ((xtop = xml_new("rpc", NULL, NULL)) == NULL) goto done; xbot = xtop; + /* XXX: something strange for rpc user */ if (api_path2xml(oppath, yspec, xtop, 1, &xbot, &y) < 0) goto done; +#if 1 + { + cbuf *c = cbuf_new(); + clicon_xml2cbuf(c, xtop, 0, 0); + clicon_debug(1, "%s xinput:%s", __FUNCTION__, cbuf_get(c)); + cbuf_free(c); + } +#endif if (data && strlen(data)){ /* Parse input data as json or xml into xml */ if (parse_xml){ diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index dd913329..105b8661 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1500,11 +1500,9 @@ api_path2xml_vec(char **vec, name = local; } if (y0->yn_keyword == Y_SPEC){ /* top-node */ - clicon_debug(1, "%s 1 %s", __FUNCTION__, name); y = yang_find_topnode((yang_spec*)y0, name, schemanode); } else { - clicon_debug(1, "%s 2 %s", __FUNCTION__, name); y = schemanode?yang_find_schemanode((yang_node*)y0, name): yang_find_datanode((yang_node*)y0, name); } @@ -1593,7 +1591,6 @@ api_path2xml_vec(char **vec, * @param[in] schemanode If set use schema nodes otherwise data nodes. * @param[out] xbotp Resulting xml tree (end of xpath) * @param[out] ybotp Yang spec matching xbotp - * @see api_path2xml_vec * @example * api_path: /subif-entry=foo/subid * xtop[in] @@ -1602,6 +1599,7 @@ api_path2xml_vec(char **vec, * * xbotp: * ybotp: Y_LEAF subid + * @see api_path2xml_vec */ int api_path2xml(char *api_path, @@ -1615,7 +1613,6 @@ api_path2xml(char *api_path, char **vec = NULL; int nvec; - clicon_debug(1, "%s 0", __FUNCTION__); if (*api_path!='/'){ clicon_err(OE_DB, 0, "Invalid key: %s", api_path); goto done;