diff --git a/.gitignore b/.gitignore index dba7ee01..dd071d13 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ util/clixon_util_xml util/clixon_util_xpath util/clixon_util_yang +test/config.sh test/site.sh test/vagrant/site.mk test/cicd/site.mk diff --git a/CHANGELOG.md b/CHANGELOG.md index 308c0786..b95e0996 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ Expected: July 2020 * Added a prefix for cli_show_config/cli_show_auto so that it can produce parseable output * Thanks dcornejo@netgate.com for trying it out and suggestions * Embedding restconf into the existing [libevhtp](https://github.com/criticalstack/libevhtp) embedded web server. (Experimental). - * The existing FCGI restconf solution will continue to be supported for NGINX and other reverese proxies with an fast CGI API. + * The existing FCGI restconf solution will continue to be supported for NGINX and other reverse proxies with a FCGI API. * The restconf code has been refactored to support both modes. Hopefully, it should be straightforward to add another embedded server, such as GNU microhttpd. * The new restconf module is selected using a compile-time autotools configure as follows: * `--with-restconf=fcgi FCGI interface for stand-alone web rev-proxy eg nginx (default)` @@ -45,6 +45,11 @@ Expected: July 2020 * New clixon-config@2020-06-17.yang revision * Added CLICON_CLI_LINES_DEFAULT for setting window row size of raw terminals * Added enum HIDE to CLICON_CLI_GENMODEL for auto-cli + * Added SSL cert info for evhtp restconf https: + * CLICON_SSL_SERVER_CERT + * CLICON_SSL_SERVER_KEY + * CLICON_SSL_CA_CERT + * Restconf FCGI (eg via nginx) have changed reply message syntax slightly as follows (due to refactoring and common code with evhtp): * Bodies in error reyruns including html code have been removed * Some (extra) CRLF:s have been removed diff --git a/apps/restconf/restconf_main_evhtp.c b/apps/restconf/restconf_main_evhtp.c index 8519ddb2..7615e808 100644 --- a/apps/restconf/restconf_main_evhtp.c +++ b/apps/restconf/restconf_main_evhtp.c @@ -35,9 +35,6 @@ * libevhtp code */ -/* XXX temp constant should go away, */ -#undef _EVHTP_NYI - #ifdef HAVE_CONFIG_H #include "clixon_config.h" /* generated by config & autoconf */ #endif @@ -82,7 +79,7 @@ #include "restconf_root.h" /* Command line options to be passed to getopt(3) */ -#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:P:s" +#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:P:sc" /* Need global variable to for signal handler XXX */ static clicon_handle _CLICON_HANDLE = NULL; @@ -100,9 +97,7 @@ restconf_sig_term(int arg) else exit(-1); if (_CLICON_HANDLE){ -#ifdef _EVHTP_NYI - stream_child_freeall(_CLICON_HANDLE); -#endif + // stream_child_freeall(_CLICON_HANDLE); restconf_terminate(_CLICON_HANDLE); } clicon_exit_set(); /* checked in clixon_event_loop() */ @@ -116,9 +111,6 @@ restconf_sig_child(int arg) int pid; if ((pid = waitpid(-1, &status, 0)) != -1 && WIFEXITED(status)){ -#ifdef _EVHTP_NYI - ; -#endif } } @@ -267,11 +259,16 @@ evhtp_params_set(clicon_handle h, evhtp_request_t *req, cvec *qvec) { - int retval = -1; - htp_method meth; - evhtp_uri_t *uri; - evhtp_path_t *path; + int retval = -1; + htp_method meth; + evhtp_uri_t *uri; + evhtp_path_t *path; + evhtp_ssl_t *ssl = NULL; + char *subject; + cvec *cvv = NULL; + char *cn; + if ((uri = req->uri) == NULL){ clicon_err(OE_DAEMON, EFAULT, "No uri"); goto done; @@ -303,9 +300,18 @@ evhtp_params_set(clicon_handle h, goto fail; } clicon_debug(1, "%s conn->ssl:%d", __FUNCTION__, req->conn->ssl?1:0); - if (req->conn->ssl != NULL){ + if ((ssl = req->conn->ssl) != NULL){ if (restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */ goto done; + /* SSL subject fields, eg CN (Common Name) , can add more here? */ + if ((subject = (char*)htp_sslutil_subject_tostr(req->conn->ssl)) != NULL){ + if (str2cvec(subject, '/', '=', &cvv) < 0) + goto done; + if ((cn = cvec_find_str(cvv, "CN")) != NULL){ + if (restconf_param_set(h, "SSL_CN", cn) < 0) + goto done; + } + } } /* Translate all http headers by capitalizing, prepend w HTTP_ and - -> _ @@ -315,6 +321,8 @@ evhtp_params_set(clicon_handle h, goto done; retval = 1; done: + if (cvv) + cvec_free(cvv); return retval; fail: retval = 0; @@ -423,6 +431,8 @@ cx_path_restconf(evhtp_request_t *req, /* 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 */ if ((qvec = cvec_new(0)) ==NULL){ @@ -446,51 +456,93 @@ cx_path_restconf(evhtp_request_t *req, } /*! Get Server cert info - * @param[out] ssl_config + * @param[in] h Clicon handle + * @param[out] ssl_config evhtp ssl config struct */ static int -get_servercerts(evhtp_ssl_cfg_t *ssl_config, - const char *pki_dir, - const char *ssl_server_cert) +cx_get_certs(clicon_handle h, + int ssl_verify_clients, + evhtp_ssl_cfg_t *ssl_config) { int retval = -1; - cbuf *cb = NULL; struct stat f_stat; + char *filename; if (ssl_config == NULL){ - clicon_err(OE_CFG, EINVAL, "ssl_config is NULL"); + clicon_err(OE_CFG, EINVAL, "Input parameter is NULL"); goto done; } - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); + if ((filename = clicon_option_str(h, "CLICON_SSL_SERVER_CERT")) == NULL){ + clicon_err(OE_CFG, EFAULT, "CLICON_SSL_SERVER_CERT option missing"); goto done; } - cprintf(cb, "%s/%s-crt.pem", pki_dir, ssl_server_cert); - if ((ssl_config->pemfile = strdup(cbuf_get(cb))) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); + if ((ssl_config->pemfile = strdup(filename)) == NULL){ + clicon_err(OE_CFG, errno, "strdup"); goto done; } if (stat(ssl_config->pemfile, &f_stat) != 0) { clicon_err(OE_FATAL, errno, "Cannot load SSL cert '%s'", ssl_config->pemfile); goto done; } - cbuf_reset(cb); - cprintf(cb, "%s/%s-key.pem", pki_dir, ssl_server_cert); - if ((ssl_config->privfile = strdup(cbuf_get(cb))) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); + if ((filename = clicon_option_str(h, "CLICON_SSL_SERVER_KEY")) == NULL){ + clicon_err(OE_CFG, EFAULT, "CLICON_SSL_SERVER_KEY option missing"); + goto done; + } + if ((ssl_config->privfile = strdup(filename)) == NULL){ + clicon_err(OE_CFG, errno, "strdup"); goto done; } if (stat(ssl_config->privfile, &f_stat) != 0) { clicon_err(OE_FATAL, errno, "Cannot load SSL key '%s'", ssl_config->privfile); goto done; } + if (ssl_verify_clients){ + if ((filename = clicon_option_str(h, "CLICON_SSL_CA_CERT")) == NULL){ + clicon_err(OE_CFG, EFAULT, "CLICON_SSL_CA_CERT option missing"); + goto done; + } + if ((ssl_config->cafile = strdup(filename)) == NULL){ + clicon_err(OE_CFG, errno, "strdup"); + goto done; + } + if (stat(ssl_config->cafile, &f_stat) != 0) { + clicon_err(OE_FATAL, errno, "Cannot load SSL key '%s'", ssl_config->privfile); + goto done; + } + } retval = 0; done: - if (cb) - cbuf_free(cb); return retval; } +static int +cx_verify_certs(int pre_verify, + evhtp_x509_store_ctx_t *store) +{ +#ifdef NOTYET + char buf[256]; + X509 * err_cert; + int err; + int depth; + SSL * ssl; + evhtp_connection_t * connection; + evhtp_ssl_cfg_t * ssl_cfg; + + fprintf(stderr, "%s %d\n", __FUNCTION__, pre_verify); + + err_cert = X509_STORE_CTX_get_current_cert(store); + err = X509_STORE_CTX_get_error(store); + depth = X509_STORE_CTX_get_error_depth(store); + ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()); + + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); + + connection = SSL_get_app_data(ssl); + ssl_cfg = connection->htp->ssl_cfg; +#endif + return pre_verify; +} + /*! Usage help routine * @param[in] argv0 command line * @param[in] h Clicon handle @@ -513,6 +565,7 @@ usage(clicon_handle h, "\t-u \t Internal socket domain path or IP addr (see -a)\n" "\t-o \"