diff --git a/apps/restconf/README.md b/apps/restconf/README.md index 482670e3..260704a4 100644 --- a/apps/restconf/README.md +++ b/apps/restconf/README.md @@ -12,9 +12,9 @@ There are two installation instructions: for libevhtp and nginx. Download, build and install libevhtp from source. Prereqs: libevent and cmake. ``` - git clone https://github.com/clicon/libevhtp.git - cd libevhtp/build - cmake -DEVHTP_DISABLE_REGEX=ON -DEVHTP_DISABLE_EVTHR=ON .. + sudo git clone https://github.com/clicon/clixon-libevhtp.git + cd clixon-libevhtp + ./configure --libdir=/usr/lib make sudo make install ``` diff --git a/apps/restconf/restconf_api_evhtp.c b/apps/restconf/restconf_api_evhtp.c index e36322b1..37f6ea9d 100644 --- a/apps/restconf/restconf_api_evhtp.c +++ b/apps/restconf/restconf_api_evhtp.c @@ -50,6 +50,9 @@ #include /* evhtp */ +#define EVHTP_DISABLE_REGEX +#define EVHTP_DISABLE_EVTHR +#define EVHTP_EXPORT #include /* cligen */ diff --git a/apps/restconf/restconf_main_evhtp.c b/apps/restconf/restconf_main_evhtp.c index 74904271..3c2ca75e 100644 --- a/apps/restconf/restconf_main_evhtp.c +++ b/apps/restconf/restconf_main_evhtp.c @@ -134,6 +134,7 @@ #include #include #include +#include #include #include #include @@ -151,6 +152,9 @@ /* evhtp */ #include /* evbuffer */ +#define EVHTP_DISABLE_REGEX +#define EVHTP_DISABLE_EVTHR +#define EVHTP_EXPORT #include #include /* XXX inline this / use SSL directly */ @@ -213,28 +217,24 @@ openspec_handle_set(clicon_handle h, * see also this function in restcont_api_openssl.c */ static ssize_t -evbuf_write(struct evbuffer *ev, - int s, - SSL *ssl) +buf_write(char *buf, + size_t buflen, + int s, + SSL *ssl) { ssize_t retval = -1; - size_t buflen; - unsigned char *buf; int ret; - if ((buflen = evbuffer_get_length(ev)) > 0){ - buf = evbuffer_pullup(ev, -1); - if (ssl){ - if ((ret = SSL_write(ssl, buf, buflen)) <= 0){ - clicon_err(OE_SSL, 0, "SSL_write"); - goto done; - } + if (ssl){ + if ((ret = SSL_write(ssl, buf, buflen)) <= 0){ + clicon_err(OE_SSL, 0, "SSL_write"); + goto done; } - else{ - if (write(s, buf, buflen) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } + } + else{ + if (write(s, buf, buflen) < 0){ + clicon_err(OE_UNIX, errno, "write"); + goto done; } } retval = 0; @@ -787,6 +787,25 @@ close_openssl_socket(int s, return retval; } +/*! Send initial bad request reply before actual packet received, just after accept + * @param[in] ssl if set, it will be freed + */ +static int +accept_badrequest(clicon_handle h, + int s, + SSL *ssl) +{ + int retval = -1; + char *buf = "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\nContent-Type: text/plain\r\n\r\n"; + + clicon_debug(1, "%s", __FUNCTION__); + if (buf_write(buf, strlen(buf)+1, s, ssl) < 0) + goto done; + retval = 0; + done: + return retval; +} + /*! New data connection after accept, receive and reply on data sockte * * @param[in] s Socket where message arrived. read from this. @@ -798,7 +817,7 @@ close_openssl_socket(int s, */ static int restconf_connection(int s, - void* arg) + void *arg) { int retval = -1; evhtp_connection_t *conn = NULL; @@ -830,6 +849,7 @@ restconf_connection(int s, } } if (n == 0){ + clicon_debug(1, "%s n=0 closing socket", __FUNCTION__); ssl = conn->ssl; conn->ssl = NULL; evhtp_connection_free(conn); /* evhtp */ @@ -841,27 +861,43 @@ restconf_connection(int s, * signature: */ if (connection_parse_nobev(buf, n, conn) < 0){ - evhtp_request_t *er; - er = evhtp_request_new(NULL, NULL); - er->conn = conn; - conn->request = er; - htparser_set_major(conn->parser, '\1'); - htparser_set_minor(conn->parser, '\1'); - if (restconf_badrequest(h, er) < 0) + clicon_debug(1, "%s connection_parse error", __FUNCTION__); + if (accept_badrequest(h, s, conn->ssl) < 0) goto done; - if (conn->bev != NULL) - if (evbuf_write(bufferevent_get_output(conn->bev), s, NULL) < 0) - goto done; - evhtp_connection_free(conn); + SSL_free(ssl); if (close_openssl_socket(s, NULL) < 0) goto done; + conn->ssl = NULL; + evhtp_connection_free(conn); goto ok; } if (conn->bev != NULL){ - /* This is for 100 Continue, typically bev is set but output is empty - * if sent by fini callback - */ - if (evbuf_write(bufferevent_get_output(conn->bev), conn->sock, conn->ssl) < 0) + char *buf = NULL; + size_t buflen; + struct evbuffer *ev; + + if ((ev = bufferevent_get_output(conn->bev)) != NULL){ + buf = (char*)evbuffer_pullup(ev, -1); + buflen = evbuffer_get_length(ev); + if (buflen){ + if (buf_write(buf, buflen, conn->sock, conn->ssl) < 0) + goto done; + } + else{ + clicon_debug(1, "%s bev is NULL 1", __FUNCTION__); + if (accept_badrequest(h, s, conn->ssl) < 0) /* actually error */ + goto done; + } + } + else{ + clicon_debug(1, "%s bev is NULL 2", __FUNCTION__); + if (accept_badrequest(h, s, conn->ssl) < 0) /* actually error */ + goto done; + } + } + else{ + clicon_debug(1, "%s bev is NULL 3", __FUNCTION__); + if (accept_badrequest(h, s, conn->ssl) < 0) /* actually error */ goto done; } ok: @@ -935,43 +971,6 @@ restconf_checkcert_file(cxobj *xrestconf, return retval; } -/*! Send initial bad request reply before actual packet received, just after accept - * @param[in] ssl if set, it will be freed - */ -static int -accept_badrequest(clicon_handle h, - int s, - SSL *ssl, - evhtp_connection_t *conn) -{ - int retval = -1; - evhtp_request_t *er; - - /* - * Since message has not been read, the reply is constructed manually - * using http 1.1 - * See note (1) http to https port: - */ - er = evhtp_request_new(NULL, NULL); - er->conn = conn; - conn->request = er; - conn->ssl = NULL; - htparser_set_major(conn->parser, '\1'); - htparser_set_minor(conn->parser, '\1'); - if (restconf_badrequest(h, er) < 0) - goto done; - /* XXX: should there be a body? */ - if (conn->bev != NULL) - if (evbuf_write(bufferevent_get_output(conn->bev), s, ssl) < 0) - goto done; - evhtp_connection_free(conn); - if (close_openssl_socket(s, ssl) < 0) - goto done; - retval = 0; - done: - return retval; -} - /*! Accept new socket client * @param[in] fd Socket (unix or ip) * @param[in] arg typecast clicon_handle @@ -993,7 +992,8 @@ restconf_accept_client(int fd, int ret; evhtp_t *evhtp = NULL; evhtp_connection_t *conn; - + int e; + clicon_debug(1, "%s %d", __FUNCTION__, fd); if (rsock == NULL){ clicon_err(OE_YANG, EINVAL, "rsock is NULL"); @@ -1052,17 +1052,23 @@ restconf_accept_client(int fd, * Both error cases: Call SSL_get_error() with the return value ret */ if ((ret = SSL_accept(ssl)) != 1) { - int e = SSL_get_error(ssl, ret); + e = SSL_get_error(ssl, ret); + switch (e){ - case SSL_ERROR_SSL: { /* 1 */ - if (accept_badrequest(h, s, NULL, conn) < 0) + case SSL_ERROR_SSL: /* 1 */ + clicon_debug(1, "%s SSL_ERROR_SSL (not ssl mesage on ssl socket)", __FUNCTION__); + if (accept_badrequest(h, s, NULL) < 0) goto done; SSL_free(ssl); + if (close_openssl_socket(s, NULL) < 0) + goto done; + conn->ssl = NULL; + evhtp_connection_free(conn); goto ok; - } break; case SSL_ERROR_SYSCALL: /* 5 */ /* look at error stack/return value/errno */ + clicon_debug(1, "%s SSL_ERROR_SYSCALL", __FUNCTION__); SSL_free(ssl); if (close_openssl_socket(s, NULL) < 0) goto done; @@ -1092,8 +1098,18 @@ restconf_accept_client(int fd, */ if (restconf_auth_type_get(h) == CLIXON_AUTH_CLIENT_CERTIFICATE && SSL_get_peer_certificate(ssl) == NULL) { /* Get certificates (if available) */ - if (accept_badrequest(h, s, ssl, conn) < 0) + if (accept_badrequest(h, s, ssl) < 0) goto done; + if (ssl){ + if ((ret = SSL_shutdown(ssl)) < 0){ + int e = SSL_get_error(ssl, ret); + clicon_err(OE_SSL, 0, "SSL_shutdown, err:%d", e); + goto done; + } + SSL_free(ssl); + } + // close(s); Error (56 != 0) in Test14 [No cert: certificate required]: + // clixon_event_unreg_fd(s, restconf_connection); goto ok; } /* Get the actual peer, XXX this maybe could be done in ca-auth client-cert code ? diff --git a/configure b/configure index 6a2f0469..f7b5c9e9 100755 --- a/configure +++ b/configure @@ -4992,7 +4992,6 @@ else with_restconf=fcgi fi - # Actions for each specific package if test "x${with_restconf}" == xfcgi; then # Lives in libfcgi-dev @@ -5187,7 +5186,12 @@ fi for ac_header in evhtp/evhtp.h do : - ac_fn_c_check_header_mongrel "$LINENO" "evhtp/evhtp.h" "ac_cv_header_evhtp_evhtp_h" "$ac_includes_default" + ac_fn_c_check_header_compile "$LINENO" "evhtp/evhtp.h" "ac_cv_header_evhtp_evhtp_h" "$ac_includes_default + #define EVHTP_DISABLE_REGEX + #define EVHTP_DISABLE_EVTHR + #define EVHTP_EXPORT + +" if test "x$ac_cv_header_evhtp_evhtp_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_EVHTP_EVHTP_H 1 @@ -5246,7 +5250,6 @@ else as_fn_error $? "libevhtp missing" "$LINENO" 5 fi - elif test "x${with_restconf}" == xno; then # Cant get around "no" as an answer for --without-restconf that is reset here to undefined with_restconf= @@ -5254,6 +5257,7 @@ else as_fn_error $? "No such restconf package: ${with_restconf}" "$LINENO" 5 fi + if test "x${with_restconf}" != "x"; then # This is so it appears in config.h diff --git a/configure.ac b/configure.ac index 641eac97..b6df8683 100644 --- a/configure.ac +++ b/configure.ac @@ -205,7 +205,6 @@ AC_ARG_WITH([restconf], AS_HELP_STRING([--with-restconf=fcgi],[FCGI interface for stand-alone web rev-proxy eg nginx (default)]), , [with_restconf=fcgi]) - # Actions for each specific package if test "x${with_restconf}" == xfcgi; then # Lives in libfcgi-dev @@ -214,9 +213,15 @@ elif test "x${with_restconf}" == xevhtp; then AC_CHECK_LIB(ssl, OPENSSL_init_ssl ,, AC_MSG_ERROR([libssl missing])) AC_CHECK_LIB(crypto, CRYPTO_new_ex_data, , AC_MSG_ERROR([libcrypto missing])) AC_CHECK_LIB(event, event_init,, AC_MSG_ERROR([libevent missing])) - AC_CHECK_HEADERS(evhtp/evhtp.h,, AC_MSG_ERROR([evhtp header missing. See https://github.com/clicon/libevhtp])) + AC_CHECK_HEADERS(evhtp/evhtp.h, + [], + AC_MSG_ERROR([evhtp header missing. See https://github.com/clicon/libevhtp]), + [AC_INCLUDES_DEFAULT[ + #define EVHTP_DISABLE_REGEX + #define EVHTP_DISABLE_EVTHR + #define EVHTP_EXPORT + ]]) AC_CHECK_LIB(evhtp, evhtp_new,, AC_MSG_ERROR([libevhtp missing]),[-lpthread -levent -levent_openssl -lssl -lcrypto]) - elif test "x${with_restconf}" == xno; then # Cant get around "no" as an answer for --without-restconf that is reset here to undefined with_restconf= @@ -224,6 +229,7 @@ else AC_MSG_ERROR([No such restconf package: ${with_restconf}]) fi + if test "x${with_restconf}" != "x"; then # This is so it appears in config.h AC_DEFINE_UNQUOTED(WITH_RESTCONF, ${with_restconf}, [Restconf package]) diff --git a/docker/main/Dockerfile.evhtp b/docker/main/Dockerfile.evhtp index 03a8b86d..e4d56f0f 100644 --- a/docker/main/Dockerfile.evhtp +++ b/docker/main/Dockerfile.evhtp @@ -41,15 +41,15 @@ RUN apk add --update git make build-base gcc flex bison curl-dev # Create a directory to hold source-code, dependencies etc RUN mkdir /clixon -# libevht +# evhtp # dependencies -RUN apk add --update libevent cmake libevent-dev +RUN apk add --update libevent libevent-dev # clone libevhtp WORKDIR /clixon RUN git clone https://github.com/clicon/clixon-libevhtp.git -WORKDIR /clixon/libevhtp +WORKDIR /clixon/clixon-libevhtp RUN ./configure RUN make diff --git a/example/main/example_restconf.c b/example/main/example_restconf.c index ce00c98b..04e9e397 100644 --- a/example/main/example_restconf.c +++ b/example/main/example_restconf.c @@ -254,7 +254,7 @@ example_basic_auth(clicon_handle h, user=NULL; /* to avoid free below */ retval = 1; done: /* error */ - clicon_debug(1, "%s retval:%d authp:%s", __FUNCTION__, retval, *authp); + clicon_debug(1, "%s retval:%d authp:%s", __FUNCTION__, retval, authp?"":*authp); if (user) free(user); if (cb) @@ -324,7 +324,7 @@ example_no_auth(clicon_handle h, user=NULL; /* to avoid free below */ retval = 1; done: /* error */ - clicon_debug(1, "%s retval:%d authp:%s", __FUNCTION__, retval, *authp); + clicon_debug(1, "%s retval:%d authp:%s", __FUNCTION__, retval, authp?"":*authp); if (user) free(user); if (cb) diff --git a/test/test_nacm_default.sh b/test/test_nacm_default.sh index ca3be682..9e57ea62 100755 --- a/test/test_nacm_default.sh +++ b/test/test_nacm_default.sh @@ -105,10 +105,9 @@ EOF new "wait backend" wait_backend - else - new "Restart backend as eg follows: -Ff $cfg -s $db" - sleep 2 fi + new "wait backend" + wait_backend if [ $RC -ne 0 ]; then # Bring your own restconf new "kill old restconf daemon" @@ -116,10 +115,9 @@ EOF new "start restconf daemon" start_restconf -f $cfg - - new "wait restconf" - wait_restconf fi + new "wait restconf" + wait_restconf # Use POST (instead of startup) # Note this only works because CLICON_NACM_DISABLED_ON_EMPTY is true @@ -177,9 +175,12 @@ EOF status="HTTP/1.1 200 OK" ;; esac + new "get 99" expectpart "$(curl -u guest:bar $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/nacm-example:x)" 0 "$status" "$ret" + sleep $DEMSLEEP + if [ $RC -ne 0 ]; then # Bring your own restconf new "Kill restconf daemon" stop_restconf diff --git a/test/test_nacm_module_write.sh b/test/test_nacm_module_write.sh index 4dde48b5..e20beb27 100755 --- a/test/test_nacm_module_write.sh +++ b/test/test_nacm_module_write.sh @@ -184,10 +184,10 @@ nacm # delete | p/d | xp/dx | p/d # replace all, then must include NACM rules as well +# This usually triggers a 'HTTP/1.1 100 Continue' from curl as well MSG="$RULES" new "update root list permit" expectpart "$(curl -u andy:bar $CURLOPTS -H 'Content-Type: application/yang-data+xml' -X PUT $RCPROTO://localhost/restconf/data -d "$MSG")" 0 'HTTP/1.1 204 No Content' -# Usually a 'HTTP/1.1 100 Continue' as well new "delete root list deny" expectpart "$(curl -u wilma:bar $CURLOPTS -X DELETE $RCPROTO://localhost/restconf/data)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} ' diff --git a/test/vagrant/vagrant.sh b/test/vagrant/vagrant.sh index 0be17639..bee130a5 100755 --- a/test/vagrant/vagrant.sh +++ b/test/vagrant/vagrant.sh @@ -97,7 +97,6 @@ system=$($sshcmd uname) # we use the release "hack" instead # Some release have packages, some need to be built from source buildfcgi=false -buildevhtp=false case $release in openbsd) # packages for building @@ -130,7 +129,7 @@ case $release in $sshcmd sudo pkg install -y fcgi-devkit nginx ;; evhtp) - $sshcmd sudo pkg install -y libevent libevhtp + $sshcmd sudo pkg install -y libevent ;; esac ;; @@ -157,7 +156,6 @@ case $release in ;; evhtp) $sshcmd sudo yum install -y libevent openssl - buildevhtp=true $sshcmd sudo yum install -y libevent-devel openssl-devel ;; esac @@ -203,7 +201,6 @@ case $release in ;; evhtp) # $sshcmd sudo apt install -y libevent-2.1 - buildevhtp=true $sshcmd sudo apt install -y libevent-dev libssl-dev ;; esac @@ -257,15 +254,13 @@ case ${with_restconf} in . ./nginx.sh $dir $idfile $port $wwwuser ;; evhtp) - if $buildevhtp; then - $sshcmd << 'EOF' - test -d libevhtp || git clone https://github.com/clicon/libevhtp.git - cd libevhtp; - ./configure --libdir=/usr/lib # otherwise in /usr/local/lib where RH dont look - make - sudo make install + $sshcmd << 'EOF' + test -d clixon-libevhtp || git clone https://github.com/clicon/clixon-libevhtp.git + cd clixon-libevhtp; + ./configure --libdir=/usr/lib # otherwise in /usr/local/lib where RH dont look + make + sudo make install EOF - fi ;; esac