* Restconf authentication callback (ca_auth) signature changed

* Not backward compatible: All uses of the ca-auth callback in restconf plugins must be changed
  * New version is: `int ca_auth(h, req, auth_type, authp, userp)`
    * where `auth_type` is the requested authentication-type (none, client-cert or user-defined)
    * `authp` is the returned authentication flag
    * `userp` is the returned associated authenticated user
    * and the return value is three-valued: -1: Error, 0: ignored, 1: OK
  * For more info see [clixon-docs](https://clixon-docs.readthedocs.io/en/latest/restconf.html)
* New clixon-restconf@2020-12-30.yang revision
This commit is contained in:
Olof hagsand 2021-02-09 21:15:54 +01:00
parent 1f0147f996
commit 710fc76887
54 changed files with 1216 additions and 485 deletions

View file

@ -86,6 +86,7 @@ APPSRC += backend_socket.c
APPSRC += backend_client.c
APPSRC += backend_commit.c
APPSRC += backend_plugin.c
APPSRC += backend_plugin_restconf.c # Pseudo plugin for restconf daemon
APPSRC += backend_startup.c
APPOBJ = $(APPSRC:.c=.o)

View file

@ -75,6 +75,7 @@
#include "backend_commit.h"
#include "backend_handle.h"
#include "backend_startup.h"
#include "backend_plugin_restconf.h"
/* Command line options to be passed to getopt(3) */
#define BACKEND_OPTS "hD:f:E:l:d:p:b:Fza:u:P:1qs:c:U:g:y:o:"
@ -388,169 +389,6 @@ ret2status(int ret,
return retval;
}
/*---------------------------------------------------------------------
* Restconf process pseudo plugin
*/
#define RESTCONF_PROCESS "restconf"
/*! Process rpc callback function
* - if RPC op is start, if enable is true, start the service, if false, error or ignore it
* - if RPC op is stop, stop the service
* These rules give that if RPC op is start and enable is false -> change op to none
*/
int
restconf_rpc_wrapper(clicon_handle h,
process_entry_t *pe,
char **operation)
{
int retval = -1;
cxobj *xt = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if (strcmp(*operation, "stop") == 0){
/* if RPC op is stop, stop the service */
}
else if (strcmp(*operation, "start") == 0){
/* RPC op is start & enable is true, then start the service,
& enable is false, error or ignore it */
if (xmldb_get(h, "running", NULL, "/restconf", &xt) < 0)
goto done;
if (xt != NULL &&
xpath_first(xt, NULL, "/restconf[enable='false']") != NULL) {
*operation = "none";
}
}
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
/*! Enable process-control of restconf daemon, ie start/stop restconf by registering restconf process
* @param[in] h Clicon handle
* @note Could also look in clixon-restconf and start process if enable is true, but that needs to
* be in start callback using a pseudo plugin.
*/
static int
restconf_pseudo_process_control(clicon_handle h)
{
int retval = -1;
char **argv = NULL;
int i;
int nr;
char dbgstr[8];
char wwwstr[64];
nr = 4;
if (clicon_debug_get() != 0)
nr += 2;
if ((argv = calloc(nr, sizeof(char *))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
i = 0;
snprintf(wwwstr, sizeof(wwwstr)-1, "%s/clixon_restconf", clicon_option_str(h, "CLICON_WWWDIR"));
argv[i++] = wwwstr;
argv[i++] = "-f";
argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
if (clicon_debug_get() != 0){
argv[i++] = "-D";
snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
argv[i++] = dbgstr;
}
argv[i++] = NULL;
assert(i==nr);
if (clixon_process_register(h, RESTCONF_PROCESS,
NULL /* XXX network namespace */,
restconf_rpc_wrapper,
argv, nr) < 0)
goto done;
if (argv != NULL)
free(argv);
retval = 0;
done:
return retval;
}
/*! Restconf pseduo-plugin process validate
*/
static int
restconf_pseudo_process_validate(clicon_handle h,
transaction_data td)
{
int retval = -1;
cxobj *xtarget;
clicon_debug(1, "%s", __FUNCTION__);
xtarget = transaction_target(td);
/* If ssl-enable is true and (at least a) socket has ssl,
* then server-cert-path and server-key-path must exist */
if (xpath_first(xtarget, NULL, "restconf/enable[.='true']") &&
xpath_first(xtarget, NULL, "restconf/socket[ssl='true']")){
/* Should filepath be checked? One could claim this is a runtime system,... */
if (xpath_first(xtarget, 0, "restconf/server-cert-path") == NULL){
clicon_err(OE_CFG, 0, "SSL enabled but server-cert-path not set");
return -1; /* induce fail */
}
if (xpath_first(xtarget, 0, "restconf/server-key-path") == NULL){
clicon_err(OE_CFG, 0, "SSL enabled but server-key-path not set");
return -1; /* induce fail */
}
}
retval = 0;
return retval;
}
/*! Restconf pseduo-plugin process commit
*/
static int
restconf_pseudo_process_commit(clicon_handle h,
transaction_data td)
{
int retval = -1;
cxobj *xtarget;
cxobj *cx;
int enabled = 0;
clicon_debug(1, "%s", __FUNCTION__);
xtarget = transaction_target(td);
if (xpath_first(xtarget, NULL, "/restconf[enable='true']") != NULL)
enabled++;
if ((cx = xpath_first(xtarget, NULL, "/restconf/enable")) != NULL &&
xml_flag(cx, XML_FLAG_CHANGE|XML_FLAG_ADD)){
if (clixon_process_operation(h, RESTCONF_PROCESS,
enabled?"start":"stop", 0, NULL) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Register start/stop restconf RPC and create pseudo-plugin to monitor enable flag
* @param[in] h Clixon handle
*/
static int
restconf_pseudo_process_reg(clicon_handle h,
yang_stmt *yspec)
{
int retval = -1;
clixon_plugin *cp = NULL;
if (clixon_pseudo_plugin(h, "restconf pseudo plugin", &cp) < 0)
goto done;
cp->cp_api.ca_trans_commit = restconf_pseudo_process_commit;
cp->cp_api.ca_trans_validate = restconf_pseudo_process_validate;
/* Register generic process-control of restconf daemon, ie start/stop restconf */
if (restconf_pseudo_process_control(h) < 0)
goto done;
retval = 0;
done:
return retval;
}
/* Debug timer */
int
@ -981,7 +819,7 @@ main(int argc,
goto done;
/* Check restconf start/stop from backend */
if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS")){
if (restconf_pseudo_process_reg(h, yspec) < 0)
if (backend_plugin_restconf_register(h, yspec) < 0)
goto done;
}
/* Here all modules are loaded

View file

@ -0,0 +1,223 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*
* Pseudo backend plugin for starting restconf daemon
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include "clixon_backend_transaction.h"
#include "backend_plugin_restconf.h"
/*---------------------------------------------------------------------
* Restconf process pseudo plugin
*/
#define RESTCONF_PROCESS "restconf"
/*! Process rpc callback function
* - if RPC op is start, if enable is true, start the service, if false, error or ignore it
* - if RPC op is stop, stop the service
* These rules give that if RPC op is start and enable is false -> change op to none
*/
int
restconf_rpc_wrapper(clicon_handle h,
process_entry_t *pe,
char **operation)
{
int retval = -1;
cxobj *xt = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if (strcmp(*operation, "stop") == 0){
/* if RPC op is stop, stop the service */
}
else if (strcmp(*operation, "start") == 0){
/* RPC op is start & enable is true, then start the service,
& enable is false, error or ignore it */
if (xmldb_get(h, "running", NULL, "/restconf", &xt) < 0)
goto done;
if (xt != NULL &&
xpath_first(xt, NULL, "/restconf[enable='false']") != NULL) {
*operation = "none";
}
}
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
/*! Enable process-control of restconf daemon, ie start/stop restconf by registering restconf process
* @param[in] h Clicon handle
* @note Could also look in clixon-restconf and start process if enable is true, but that needs to
* be in start callback using a pseudo plugin.
*/
static int
restconf_pseudo_process_control(clicon_handle h)
{
int retval = -1;
char **argv = NULL;
int i;
int nr;
char dbgstr[8];
char wwwstr[64];
nr = 4;
if (clicon_debug_get() != 0)
nr += 2;
if ((argv = calloc(nr, sizeof(char *))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
i = 0;
snprintf(wwwstr, sizeof(wwwstr)-1, "%s/clixon_restconf", clicon_option_str(h, "CLICON_WWWDIR"));
argv[i++] = wwwstr;
argv[i++] = "-f";
argv[i++] = clicon_option_str(h, "CLICON_CONFIGFILE");
/* Add debug if backend has debug.
* There is also a debug flag in clixon-restconf.yang but it kicks in after it starts
*/
if (clicon_debug_get() != 0){
argv[i++] = "-D";
snprintf(dbgstr, sizeof(dbgstr)-1, "%d", clicon_debug_get());
argv[i++] = dbgstr;
}
argv[i++] = NULL;
assert(i==nr);
if (clixon_process_register(h, RESTCONF_PROCESS,
NULL /* XXX network namespace */,
restconf_rpc_wrapper,
argv, nr) < 0)
goto done;
if (argv != NULL)
free(argv);
retval = 0;
done:
return retval;
}
/*! Restconf pseduo-plugin process validate
*/
static int
restconf_pseudo_process_validate(clicon_handle h,
transaction_data td)
{
int retval = -1;
cxobj *xtarget;
clicon_debug(1, "%s", __FUNCTION__);
xtarget = transaction_target(td);
/* If ssl-enable is true and (at least a) socket has ssl,
* then server-cert-path and server-key-path must exist */
if (xpath_first(xtarget, NULL, "restconf/enable[.='true']") &&
xpath_first(xtarget, NULL, "restconf/socket[ssl='true']")){
/* Should filepath be checked? One could claim this is a runtime system,... */
if (xpath_first(xtarget, 0, "restconf/server-cert-path") == NULL){
clicon_err(OE_CFG, 0, "SSL enabled but server-cert-path not set");
return -1; /* induce fail */
}
if (xpath_first(xtarget, 0, "restconf/server-key-path") == NULL){
clicon_err(OE_CFG, 0, "SSL enabled but server-key-path not set");
return -1; /* induce fail */
}
}
retval = 0;
return retval;
}
/*! Restconf pseduo-plugin process commit
*/
static int
restconf_pseudo_process_commit(clicon_handle h,
transaction_data td)
{
int retval = -1;
cxobj *xtarget;
cxobj *cx;
int enabled = 0;
clicon_debug(1, "%s", __FUNCTION__);
xtarget = transaction_target(td);
if (xpath_first(xtarget, NULL, "/restconf[enable='true']") != NULL)
enabled++;
if ((cx = xpath_first(xtarget, NULL, "/restconf/enable")) != NULL &&
xml_flag(cx, XML_FLAG_CHANGE|XML_FLAG_ADD)){
if (clixon_process_operation(h, RESTCONF_PROCESS,
enabled?"start":"stop", 0, NULL) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Register start/stop restconf RPC and create pseudo-plugin to monitor enable flag
* @param[in] h Clixon handle
*/
int
backend_plugin_restconf_register(clicon_handle h,
yang_stmt *yspec)
{
int retval = -1;
clixon_plugin *cp = NULL;
if (clixon_pseudo_plugin(h, "restconf pseudo plugin", &cp) < 0)
goto done;
cp->cp_api.ca_trans_commit = restconf_pseudo_process_commit;
cp->cp_api.ca_trans_validate = restconf_pseudo_process_validate;
/* Register generic process-control of restconf daemon, ie start/stop restconf */
if (restconf_pseudo_process_control(h) < 0)
goto done;
retval = 0;
done:
return retval;
}

View file

@ -0,0 +1,43 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*
* Pseudo backend plugin for starting restconf daemon
*/
#ifndef _BACKEND_PLUGIN_RESTCONF_H_
#define _BACKEND_PLUGIN_RESTCONF_H_
int backend_plugin_restconf_register(clicon_handle h, yang_stmt *yspec);
#endif /* _BACKEND_PLUGIN_RESTCONF_H_ */

View file

@ -61,6 +61,7 @@
/* clicon */
#include <clixon/clixon.h>
#include "restconf_lib.h"
#include "restconf_handle.h"
/* header part is copied from struct clicon_handle in lib/src/clixon_handle.c */
@ -90,6 +91,7 @@ struct restconf_handle {
/* ------ end of common handle ------ */
clicon_hash_t *rh_params; /* restconf parameters, including http headers */
clixon_auth_type_t rh_auth_type; /* authentication type */
};
/*! Creates and returns a clicon config handle for other CLICON API calls
@ -172,3 +174,33 @@ restconf_param_del_all(clicon_handle h)
done:
return retval;
}
/*! Get restconf http parameter
* @param[in] h Clicon handle
* @retval auth_type
*/
clixon_auth_type_t
restconf_auth_type_get(clicon_handle h)
{
struct restconf_handle *rh = handle(h);
return rh->rh_auth_type;
}
/*! Set restconf http parameter
* @param[in] h Clicon handle
* @param[in] name Data name
* @param[in] val Data value as null-terminated string
* @retval 0 OK
* @retval -1 Error
* Currently using clixon runtime data but there is risk for colliding names
*/
int
restconf_auth_type_set(clicon_handle h,
clixon_auth_type_t type)
{
struct restconf_handle *rh = handle(h);
rh->rh_auth_type = type;
return 0;
}

View file

@ -47,5 +47,7 @@ int restconf_handle_exit(clicon_handle h);
char *restconf_param_get(clicon_handle h, const char *param);
int restconf_param_set(clicon_handle h, const char *param, char *val);
int restconf_param_del_all(clicon_handle h);
clixon_auth_type_t restconf_auth_type_get(clicon_handle h);
int restconf_auth_type_set(clicon_handle h, clixon_auth_type_t type);
#endif /* _RESTCONF_HANDLE_H_ */

View file

@ -60,8 +60,9 @@
#include <clixon/clixon.h>
#include "restconf_api.h"
#include "restconf_handle.h"
#include "restconf_lib.h"
#include "restconf_err.h"
#include "restconf_handle.h"
/* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code
* and RFC 6241 Appendix A. NETCONF Error list
@ -495,3 +496,123 @@ restconf_drop_privileges(clicon_handle h,
return retval;
}
/*!
* @param[in] h Clicon handle
* @param[in] req Generic Www handle (can be part of clixon handle)
* @retval -1 Error
* @retval 0 Not authenticated
* @retval 1 Authenticated
*/
int
restconf_authentication_cb(clicon_handle h,
void *req,
int pretty,
restconf_media media_out)
{
int retval = -1;
clixon_auth_type_t auth_type;
int authenticated;
int ret;
char *username = NULL;
cxobj *xret = NULL;
cxobj *xerr;
auth_type = restconf_auth_type_get(h);
clicon_debug(1, "%s auth-type:%s", __FUNCTION__, clixon_auth_type_int2str(auth_type));
ret = 0;
authenticated = 0;
if (auth_type != CLIXON_AUTH_NONE)
if ((ret = clixon_plugin_auth_all(h, req,
auth_type,
&authenticated,
&username)) < 0)
goto done;
if (ret == 1){ /* OK, tag username to handle */
clicon_username_set(h, username);
}
else { /* Default behaviour */
switch (auth_type){
case CLIXON_AUTH_NONE:
clicon_username_set(h, "none");
authenticated = 1;
break;
case CLIXON_AUTH_CLIENT_CERTIFICATE: {
char *cn;
/* Check for cert subject common name (CN) */
if ((cn = restconf_param_get(h, "SSL_CN")) != NULL){
clicon_username_set(h, cn);
authenticated = 1;
}
break;
}
case CLIXON_AUTH_USER:
authenticated = 0;
break;
}
}
if (authenticated == 0){ /* Message is not authenticated (401 returned) */
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, req, xerr, pretty, media_out, 0) < 0)
goto done;
goto notauth;
}
retval = 0;
goto notauth;
}
/* If set but no user, set a dummy user */
retval = 1;
done:
clicon_debug(1, "%s retval:%d authenticated:%d user:%s",
__FUNCTION__, retval, authenticated, clicon_username_get(h));
if (xret)
xml_free(xret);
return retval;
notauth:
retval = 0;
goto done;
}
/*! Basic config init
* @param[in] h Clixon handle
* @param[in] xrestconf XML config containing clixon-restconf top-level
* @retval -1 Error
* @retval 0 Restconf is disable
* @retval 1 OK
*/
int
restconf_config_init(clicon_handle h,
cxobj *xrestconf)
{
int retval = -1;
char *enable;
cxobj *x;
char *bstr;
cvec *nsc = NULL;
clixon_auth_type_t auth_type;
if ((x = xpath_first(xrestconf, nsc, "enable")) != NULL &&
(enable = xml_body(x)) != NULL){
if (strcmp(enable, "false") == 0){
clicon_debug(1, "%s restconf disabled", __FUNCTION__);
goto disable;
}
}
/* get common fields */
if ((x = xpath_first(xrestconf, nsc, "auth-type")) != NULL &&
(bstr = xml_body(x)) != NULL){
if ((auth_type = clixon_auth_type_str2int(bstr)) < 0){
clicon_err(OE_CFG, EFAULT, "Invalid restconf auth-type: %s", bstr);
goto done;
}
restconf_auth_type_set(h, auth_type);
}
retval = 1;
done:
return retval;
disable:
retval = 0;
goto done;
}

View file

@ -82,6 +82,8 @@ int restconf_insert_attributes(cxobj *xdata, cvec *qvec);
int restconf_main_extension_cb(clicon_handle h, yang_stmt *yext, yang_stmt *ys);
char *restconf_uripath(clicon_handle h);
int restconf_drop_privileges(clicon_handle h, char *user);
int restconf_authentication_cb(clicon_handle h, void *req, int pretty, restconf_media media_out);
int restconf_config_init(clicon_handle h, cxobj *xrestconf);
#endif /* _RESTCONF_LIB_H_ */

View file

@ -83,19 +83,22 @@
#include "restconf_err.h"
#include "restconf_root.h"
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:ro:"
/* See see listen(5) */
#define SOCKET_LISTEN_BACKLOG 16
/* clixon evhtp handle */
/* Clixon evhtp handle
* Global data about evhtp lib,
* See evhtp_request_t *req for per-message state data
*/
typedef struct {
evhtp_t **eh_htpvec; /* One per socket */
int eh_htplen; /* Number of sockets */
struct event_base *eh_evbase; /* Change to list */
evhtp_ssl_cfg_t *eh_ssl_config;
clicon_handle eh_h;
evhtp_t **eh_htpvec; /* One per socket */
int eh_htplen; /* Number of sockets */
struct event_base *eh_evbase; /* Change to list */
evhtp_ssl_cfg_t *eh_ssl_config;
} cx_evhtp_handle;
/* Need this global to pass to signal handler
@ -150,7 +153,7 @@ restconf_sig_term(int arg)
exit(-1);
if (_EVHTP_HANDLE) /* global */
evhtp_terminate(_EVHTP_HANDLE);
if (_CLICON_HANDLE){
if (_CLICON_HANDLE){ /* could be replaced by eh->eh_h */
// stream_child_freeall(_CLICON_HANDLE);
restconf_terminate(_CLICON_HANDLE);
}
@ -448,8 +451,9 @@ static void
cx_path_wellknown(evhtp_request_t *req,
void *arg)
{
clicon_handle h = arg;
int ret;
cx_evhtp_handle *eh = (cx_evhtp_handle*)arg;
clicon_handle h = eh->eh_h;
int ret;
clicon_debug(1, "------------");
/* input debug */
@ -479,15 +483,15 @@ static void
cx_path_restconf(evhtp_request_t *req,
void *arg)
{
clicon_handle h = arg;
int ret;
cvec *qvec = NULL;
cx_evhtp_handle *eh = (cx_evhtp_handle*)arg;
clicon_handle h = eh->eh_h;
int ret;
cvec *qvec = NULL;
clicon_debug(1, "------------");
/* input debug */
if (clicon_debug_get())
evhtp_headers_for_each(req->headers_in, print_header, h);
/* get accepted connection */
/* Query vector, ie the ?a=x&b=y stuff */
@ -844,8 +848,7 @@ cx_evhtp_socket(clicon_handle h,
cvec *nsc,
char *server_cert_path,
char *server_key_path,
char *server_ca_cert_path,
int auth_type_client_certificate)
char *server_ca_cert_path)
{
int retval = -1;
char *netns = NULL;
@ -862,7 +865,6 @@ cx_evhtp_socket(clicon_handle h,
clicon_err(OE_UNIX, errno, "evhtp_new");
goto done;
}
#ifndef EVHTP_DISABLE_EVTHR /* threads */
evhtp_use_threads_wexit(htp, NULL, NULL, 4, NULL);
#endif
@ -871,12 +873,12 @@ cx_evhtp_socket(clicon_handle h,
/* Callback right after a connection is accepted. */
evhtp_set_post_accept_cb(htp, cx_post_accept, h);
/* Callback to be executed for all /restconf api calls */
if (evhtp_set_cb(htp, "/" RESTCONF_API, cx_path_restconf, h) == NULL){
if (evhtp_set_cb(htp, "/" RESTCONF_API, cx_path_restconf, eh) == NULL){
clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
goto done;
}
/* Callback to be executed for all /restconf api calls */
if (evhtp_set_cb(htp, RESTCONF_WELL_KNOWN, cx_path_wellknown, h) == NULL){
if (evhtp_set_cb(htp, RESTCONF_WELL_KNOWN, cx_path_wellknown, eh) == NULL){
clicon_err(OE_EVENTS, errno, "evhtp_set_cb");
goto done;
}
@ -933,41 +935,41 @@ cx_evhtp_init(clicon_handle h,
cvec *nsc,
cx_evhtp_handle *eh)
{
int retval = -1;
char* enable;
int ssl_enable = 0;
cxobj **vec = NULL;
size_t veclen;
char *server_cert_path = NULL;
char *server_key_path = NULL;
char *server_ca_cert_path = NULL;
char *auth_type = NULL;
int auth_type_client_certificate = 0;
cxobj *x;
int i;
int retval = -1;
int ssl_enable = 0;
int dbg = 0;
cxobj **vec = NULL;
size_t veclen;
char *server_cert_path = NULL;
char *server_key_path = NULL;
char *server_ca_cert_path = NULL;
cxobj *x;
char *bstr;
int i;
int ret;
clixon_auth_type_t auth_type;
clicon_debug(1, "%s", __FUNCTION__);
if ((x = xpath_first(xrestconf, nsc, "enable")) != NULL &&
(enable = xml_body(x)) != NULL){
if (strcmp(enable, "false") == 0){
clicon_debug(1, "%s restconf disabled", __FUNCTION__);
goto disable;
}
}
if ((ret = restconf_config_init(h, xrestconf)) < 0)
goto done;
if (ret == 0)
goto disable;
auth_type = restconf_auth_type_get(h);
/* If at least one socket has ssl then enable global ssl_enable */
ssl_enable = xpath_first(xrestconf, nsc, "socket[ssl='true']") != NULL;
/* get common fields */
if ((x = xpath_first(xrestconf, nsc, "auth-type")) != NULL)
auth_type = xml_body(x);
if (auth_type && strcmp(auth_type, "client-certificate") == 0)
auth_type_client_certificate = 1;
if ((x = xpath_first(xrestconf, nsc, "server-cert-path")) != NULL)
server_cert_path = xml_body(x);
if ((x = xpath_first(xrestconf, nsc, "server-key-path")) != NULL)
server_key_path = xml_body(x);
if ((x = xpath_first(xrestconf, nsc, "server-ca-cert-path")) != NULL)
server_ca_cert_path = xml_body(x);
if ((x = xpath_first(xrestconf, nsc, "debug")) != NULL &&
(bstr = xml_body(x)) != NULL){
dbg = atoi(bstr);
clicon_debug_init(dbg, NULL);
}
/* Here the daemon either uses SSL or not, ie you cant seem to mix http and https :-( */
if (ssl_enable){
/* Init evhtp ssl config struct */
@ -982,11 +984,11 @@ cx_evhtp_init(clicon_handle h,
if (cx_get_ssl_server_certs(h, server_cert_path, server_key_path, eh->eh_ssl_config) < 0)
goto done;
/* If client auth get client CA cert */
if (auth_type_client_certificate)
if (auth_type == CLIXON_AUTH_CLIENT_CERTIFICATE)
if (cx_get_ssl_client_ca_certs(h, server_ca_cert_path, eh->eh_ssl_config) < 0)
goto done;
eh->eh_ssl_config->x509_verify_cb = cx_verify_certs; /* Is extra verification necessary? */
if (auth_type_client_certificate){
if (auth_type == CLIXON_AUTH_CLIENT_CERTIFICATE){
eh->eh_ssl_config->verify_peer = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
eh->eh_ssl_config->x509_verify_cb = cx_verify_certs;
eh->eh_ssl_config->verify_depth = 2;
@ -998,8 +1000,7 @@ cx_evhtp_init(clicon_handle h,
goto done;
for (i=0; i<veclen; i++){
if (cx_evhtp_socket(h, eh, ssl_enable, vec[i], nsc,
server_cert_path, server_key_path, server_ca_cert_path,
auth_type_client_certificate) < 0)
server_cert_path, server_key_path, server_ca_cert_path) < 0)
goto done;
}
retval = 1;
@ -1318,16 +1319,24 @@ main(int argc,
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);
/* Init restconf auth-type */
restconf_auth_type_set(h, CLIXON_AUTH_NONE);
/* Dump configuration options on debug */
/* Dump configuration options on debug */
if (dbg)
clicon_option_dump(h, dbg);
/* Call start function in all plugins before we go interactive */
if (clixon_plugin_start_all(h) < 0)
goto done;
if ((eh = malloc(sizeof *eh)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(eh, 0, sizeof *eh);
eh->eh_h = h;
_EVHTP_HANDLE = eh; /* global */
/* Read config */

View file

@ -178,6 +178,7 @@ usage(clicon_handle h,
"\t-d <dir>\t Specify restconf plugin directory dir (default: %s)\n"
"\t-y <file>\t Load yang spec file (override yang main module)\n"
"\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
"\t-u <path|addr>\t Internal socket domain path or IP addr (see -a)\n"
"\t-r \t\t Do not drop privileges if run as root\n"
"\t-o \"<option>=<value>\" Give configuration option overriding config file (see clixon-config.yang)\n",
@ -215,7 +216,10 @@ main(int argc,
size_t cligen_buflen;
size_t cligen_bufthreshold;
int dbg = 0;
int drop_priveleges = 1;
int drop_privileges = 1;
cxobj *xconfig = NULL;
cxobj *xrestconf = NULL;
int ret;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@ -311,7 +315,7 @@ main(int argc,
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
case 'r':{ /* Do not drop privileges if run as root */
drop_priveleges = 0;
drop_privileges = 0;
break;
}
case 'o':{ /* Configuration option */
@ -332,7 +336,10 @@ main(int argc,
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);
/* Init restconf auth-type */
restconf_auth_type_set(h, CLIXON_AUTH_NONE);
/* Init cligen buffers */
cligen_buflen = clicon_option_int(h, "CLICON_CLI_BUF_START");
cligen_bufthreshold = clicon_option_int(h, "CLICON_CLI_BUF_THRESHOLD");
@ -418,11 +425,18 @@ main(int argc,
if (dbg)
clicon_option_dump(h, dbg);
/* Call start function in all plugins before we go interactive
*/
/* Call start function in all plugins before we go interactive */
if (clixon_plugin_start_all(h) < 0)
goto done;
xconfig = clicon_conf_xml(h); /* Get local config */
if ((xrestconf = xpath_first(xconfig, NULL, "restconf")) != NULL){
if ((ret = restconf_config_init(h, xrestconf)) < 0)
goto done;
if (ret == 0){
clicon_err(OE_DAEMON, EFAULT, "Restconf daemon disabled in config");
goto done;
}
}
if ((sockpath = clicon_option_str(h, "CLICON_RESTCONF_PATH")) == NULL){
clicon_err(OE_CFG, errno, "No CLICON_RESTCONF_PATH in clixon configure file");
goto done;
@ -454,7 +468,7 @@ main(int argc,
clicon_err(OE_UNIX, errno, "chmod");
goto done;
}
if (drop_priveleges){
if (drop_privileges){
/* Drop privileges to WWWUSER if started as root */
if (restconf_drop_privileges(h, WWWUSER) < 0)
goto done;

View file

@ -374,15 +374,15 @@ api_operations(clicon_handle h,
}
/*! Process a /restconf root input, this is the root of the restconf processing
* @param[in] h Clicon handle
* @param[in] req Generic Www handle (can be part of clixon handle)
* @param[in] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
* @param[in] h Clicon handle
* @param[in] req Generic Www handle (can be part of clixon handle)
* @param[in] qvec Query parameters, ie the ?<id>=<val>&<id>=<val> stuff
* @see api_root_restconf_exact for accessing /restconf/ exact
*/
int
api_root_restconf(clicon_handle h,
void *req,
cvec *qvec)
api_root_restconf(clicon_handle h,
void *req,
cvec *qvec)
{
int retval = -1;
char *request_method = NULL; /* GET,.. */
@ -396,9 +396,8 @@ api_root_restconf(clicon_handle h,
char *media_str = NULL;
restconf_media media_out = YANG_DATA_JSON;
char *indata = NULL;
int authenticated = 0;
cxobj *xret = NULL;
cxobj *xerr;
char *username = NULL;
int ret;
clicon_debug(1, "%s", __FUNCTION__);
if (req == NULL){
@ -426,7 +425,6 @@ api_root_restconf(clicon_handle h,
}
}
clicon_debug(1, "%s ACCEPT: %s %s", __FUNCTION__, media_str, restconf_media_int2str(media_out));
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
@ -464,26 +462,10 @@ api_root_restconf(clicon_handle h,
/* If present, check credentials. See "plugin_credentials" in plugin
* See RFC 8040 section 2.5
*/
if ((authenticated = clixon_plugin_auth_all(h, req)) < 0)
if ((ret = restconf_authentication_cb(h, req, pretty, media_out)) < 0)
goto done;
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
/* If set but no user, set a dummy user */
if (authenticated){
if (clicon_username_get(h) == NULL)
clicon_username_set(h, "none");
}
else{
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, req, xerr, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
if (ret == 0)
goto ok;
}
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
if (strcmp(api_resource, "yang-library-version")==0){
if (api_yang_library_version(h, req, pretty, media_out) < 0)
goto done;
@ -538,14 +520,14 @@ api_root_restconf(clicon_handle h,
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (username)
free(username);
if (pcvec)
cvec_free(pcvec);
if (pvec)
free(pvec);
if (cb)
cbuf_free(cb);
if (xret)
xml_free(xret);
return retval;
}

View file

@ -382,13 +382,11 @@ api_stream(clicon_handle h,
cvec *pcvec = NULL; /* for rest api */
cbuf *cb = NULL;
char *indata;
int authenticated = 0;
int pretty;
restconf_media media_out = YANG_DATA_XML; /* XXX default */
cbuf *cbret = NULL;
cxobj *xret = NULL;
cxobj *xerr;
int s = -1;
int ret;
#ifdef STREAM_FORK
int pid;
struct stream_child *sc;
@ -430,26 +428,10 @@ api_stream(clicon_handle h,
/* If present, check credentials. See "plugin_credentials" in plugin
* See RFC 8040 section 2.5
*/
if ((authenticated = clixon_plugin_auth_all(h, req)) < 0)
if ((ret = restconf_authentication_cb(h, req, pretty, media_out)) < 0)
goto done;
clicon_debug(1, "%s auth:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
/* If set but no user, we set a dummy user */
if (authenticated){
if (clicon_username_get(h) == NULL)
clicon_username_set(h, "none");
}
else{
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
goto done;
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, req, xerr, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
if (ret == 0)
goto ok;
}
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
if (restconf_stream(h, req, method, qvec, pretty, media_out, &s) < 0)
goto done;
if (s != -1){
@ -465,8 +447,6 @@ api_stream(clicon_handle h,
cbuf_free(cb);
if (cbret)
cbuf_free(cbret);
if (xret)
xml_free(xret);
#endif /* STREAM_FORK */
/* Listen to backend socket */
if (clixon_event_reg_fd(s,
@ -523,7 +503,5 @@ api_stream(clicon_handle h,
cbuf_free(cb);
if (cbret)
cbuf_free(cbret);
if (xret)
xml_free(xret);
return retval;
}