Restconf http-data server updates

Check data paths for .., ~ and soft links
Changed semantics of `CLICON_HTTP_DATA_PATH` and `_ROOT`
Change URI catch-all to 404 instead of 400
Fixed some memory leaks
This commit is contained in:
Olof hagsand 2022-04-24 09:44:27 +02:00
parent 0ed8b836b1
commit 84d88c8ad8
12 changed files with 303 additions and 106 deletions

View file

@ -86,32 +86,34 @@ static const map_str2str mime_map[] = {
/*! Check if uri path denotes a data path /*! Check if uri path denotes a data path
* *
* @param[out] data Pointer to string where data starts if retval = 1 * @param[in] h Clixon handle
* @retval 0 No, not a data path, or not enabled * @retval 0 No, not a data path, or not enabled
* @retval 1 Yes, a data path and "data" points to www-data if given * @retval 1 Yes, a data path and "data" points to www-data if given
*/ */
int int
api_path_is_data(clicon_handle h, api_path_is_data(clicon_handle h)
char **data)
{ {
char *path; int retval = 0;
char *path = NULL;
char *http_data_path; char *http_data_path;
if (restconf_http_data_get(h) == 0) if (restconf_http_data_get(h) == 0)
return 0; goto done;
if ((path = restconf_uripath(h)) == NULL) if ((path = restconf_uripath(h)) == NULL)
return 0; goto done;
if ((http_data_path = clicon_option_str(h, "CLICON_HTTP_DATA_PATH")) == NULL) if ((http_data_path = clicon_option_str(h, "CLICON_HTTP_DATA_PATH")) == NULL)
return 0; goto done;
if (strlen(path) < strlen(http_data_path)) if (strlen(path) < strlen(http_data_path))
return 0; goto done;
if (path[0] != '/') if (path[0] != '/')
return 0; goto done;
if (strncmp(path, http_data_path, strlen(http_data_path)) != 0) if (strncmp(path, http_data_path, strlen(http_data_path)) != 0)
return 0; goto done;
if (data) retval = 1;
*data = path + strlen(http_data_path); done:
return 1; if (path)
free(path);
return retval;
} }
/*! Generic restconf error function on get/head request /*! Generic restconf error function on get/head request
@ -156,6 +158,75 @@ api_http_data_err(clicon_handle h,
return retval; return retval;
} }
/*! Check validity of path, may only be regular dir or file
* No .., soft link, ~, etc
* @param[in] prefix Prefix of path0, where to start file check
* @param[in] path0 Filepath
* @param[out] code Error code, if retval = 0
* @retval -1 Error
* @retval 0 Invalid, code set
* @retval 1 OK
*/
static int
check_file_path(char *prefix,
char *path0,
int *code)
{
int retval = -1;
char *path = NULL;
int i;
struct stat fstat;
if (prefix == NULL || path0 == NULL || code == NULL){
clicon_err(OE_UNIX, EINVAL, "prefix, path0 or code is NULL");
goto done;
}
if ((path = strdup(path0)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
for (i=strlen(prefix); i<strlen(path0); i++){
if (path[i] == '/'){ /* Check valid dir */
path[i] = '\0';
/* Ensure not soft link */
if (lstat(path, &fstat) < 0){
*code = 404;
goto invalid;
}
if (!S_ISDIR(fstat.st_mode)){
*code = 403;
goto invalid;
}
path[i] = '/';
}
else if (path[i] == '~'){
*code = 403;
goto invalid;
}
else if (path[i] == '.' && i>strlen(prefix) && path[i-1] == '.'){
*code = 403;
goto invalid;
}
}
/* Resulting file (ensure not soft link) */
if (lstat(path, &fstat) < 0){
*code = 404;
goto invalid;
}
if (!S_ISREG(fstat.st_mode)){
*code = 403;
goto invalid;
}
retval = 1; /* OK */
done:
if (path)
free(path);
return retval;
invalid:
retval = 0;
goto done;
}
/*! Read file data request /*! Read file data request
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] req Generic Www handle (can be part of clixon handle) * @param[in] req Generic Www handle (can be part of clixon handle)
@ -167,21 +238,23 @@ api_http_data_err(clicon_handle h,
*/ */
static int static int
api_http_data_file(clicon_handle h, api_http_data_file(clicon_handle h,
void *req, void *req,
char *pathname, char *pathname,
int head) int head)
{ {
int retval = -1; int retval = -1;
cbuf *cbfile = NULL; cbuf *cbfile = NULL;
char *filename; char *filename;
struct stat fstat;
cbuf *cbdata = NULL; cbuf *cbdata = NULL;
FILE *f = NULL; FILE *f = NULL;
long fsize; long fsize;
size_t sz;
char *www_data_root = NULL; char *www_data_root = NULL;
char *suffix; char *suffix;
char *media; char *media;
int ret;
int code = 0;
char *str = NULL;
size_t sz;
if ((cbfile = cbuf_new()) == NULL){ if ((cbfile = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");
@ -204,8 +277,12 @@ api_http_data_file(clicon_handle h,
if (pathname) if (pathname)
cprintf(cbfile, "/%s", pathname); cprintf(cbfile, "/%s", pathname);
filename = cbuf_get(cbfile); filename = cbuf_get(cbfile);
if (stat(filename, &fstat) < 0){ clicon_debug(1, "%s %s", __FUNCTION__, filename);
if (api_http_data_err(h, req, 404) < 0) /* not found */ if ((ret = check_file_path(www_data_root, filename, &code)) < 0)
goto done;
if (ret == 0){
clicon_debug(1, "%s code:%d", __FUNCTION__, code);
if (api_http_data_err(h, req, code) < 0)
goto done; goto done;
goto ok; goto ok;
} }
@ -214,6 +291,9 @@ api_http_data_file(clicon_handle h,
goto done; goto done;
goto ok; goto ok;
} }
/* Size could have been taken from stat() but this reduces the race condition interval
* There is still one without flock
*/
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
fsize = ftell(f); fsize = ftell(f);
fseek(f, 0, SEEK_SET); /* same as rewind(f); */ fseek(f, 0, SEEK_SET); /* same as rewind(f); */
@ -221,31 +301,28 @@ api_http_data_file(clicon_handle h,
clicon_err(OE_UNIX, errno, "cbuf_new_alloc"); clicon_err(OE_UNIX, errno, "cbuf_new_alloc");
goto done; goto done;
} }
#if 0 /* Direct read but cannot set cb_len via API */ /* Unoptimized, no direct read but requires an extra copy,
fread(cbuf_get(cbdata), fsize, 1, f); * the cligen buf API should have some mechanism for this case without the extra copy.
#else */
{ if ((str = malloc(fsize + 1)) == NULL){
char *str; clicon_err(OE_UNIX, errno, "malloc");
if ((str = malloc(fsize + 1)) == NULL){ goto done;
clicon_err(OE_UNIX, errno, "malloc"); }
goto done; if ((sz = fread(str, fsize, 1, f)) < 0){
} clicon_err(OE_UNIX, errno, "fread");
if ((sz = fread(str, fsize, 1, f)) < 0){ goto done;
clicon_err(OE_UNIX, errno, "fread"); }
goto done; if (sz != 1){
} if (api_http_data_err(h, req, 500) < 0) /* Internal error? */
if (sz != 1){ goto done;
clicon_log(LOG_NOTICE, "%s: file read %s", __FUNCTION__, filename); goto ok;
// XXX error handling: file read }
goto done; clicon_debug(1, "%s code:%d", __FUNCTION__, code);
} str[fsize] = 0;
str[fsize] = 0; if (cbuf_append_str(cbdata, str) < 0){
if (cbuf_append_str(cbdata, str) < 0){ clicon_err(OE_UNIX, errno, "cbuf_append_str");
clicon_err(OE_UNIX, errno, "cbuf_append_str"); goto done;
goto done;
}
} }
#endif
if (restconf_reply_header(req, "Content-Type", "%s", media) < 0) if (restconf_reply_header(req, "Content-Type", "%s", media) < 0)
goto done; goto done;
if (restconf_reply_send(req, 200, cbdata, head) < 0) if (restconf_reply_send(req, 200, cbdata, head) < 0)
@ -254,6 +331,8 @@ api_http_data_file(clicon_handle h,
ok: ok:
retval = 0; retval = 0;
done: done:
if (str)
free(str);
if (f) if (f)
fclose(f); fclose(f);
if (cbfile) if (cbfile)
@ -291,8 +370,8 @@ api_http_data(clicon_handle h,
int options = 0; int options = 0;
int ret; int ret;
cbuf *indata = NULL; cbuf *indata = NULL;
char *pathname = NULL; char *path = NULL;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if (req == NULL){ if (req == NULL){
errno = EINVAL; errno = EINVAL;
@ -300,11 +379,12 @@ api_http_data(clicon_handle h,
} }
/* 1. path: with stripped prefix, ultimately: dir/filename /* 1. path: with stripped prefix, ultimately: dir/filename
*/ */
if (!api_path_is_data(h, &pathname)){ if (!api_path_is_data(h)){
if (api_http_data_err(h, req, 404) < 0) /* not found */ if (api_http_data_err(h, req, 404) < 0) /* not found */
goto done; goto done;
goto ok; goto ok;
} }
path = restconf_uripath(h);
/* 2. operation GET or HEAD */ /* 2. operation GET or HEAD */
request_method = restconf_param_get(h, "REQUEST_METHOD"); request_method = restconf_param_get(h, "REQUEST_METHOD");
if (strcmp(request_method, "GET") == 0){ if (strcmp(request_method, "GET") == 0){
@ -360,11 +440,13 @@ api_http_data(clicon_handle h,
if (restconf_reply_send(req, 200, NULL, 0) < 0) if (restconf_reply_send(req, 200, NULL, 0) < 0)
goto done; goto done;
} }
else if (api_http_data_file(h, req, pathname, head) < 0) else if (api_http_data_file(h, req, path, head) < 0)
goto done; goto done;
ok: ok:
retval = 0; retval = 0;
done: done:
if (path)
free(path);
clicon_debug(1, "%s %d", __FUNCTION__, retval); clicon_debug(1, "%s %d", __FUNCTION__, retval);
return retval; return retval;
} }

View file

@ -41,7 +41,7 @@
/* /*
* Prototypes * Prototypes
*/ */
int api_path_is_data(clicon_handle h, char **data); int api_path_is_data(clicon_handle h);
int api_http_data(clicon_handle h, void *req, cvec *qvec); int api_http_data(clicon_handle h, void *req, cvec *qvec);
#endif /* _CLIXON_HTTP_DATA_H_ */ #endif /* _CLIXON_HTTP_DATA_H_ */

View file

@ -345,7 +345,7 @@ restconf_http1_path_root(clicon_handle h,
restconf_conn *rc) restconf_conn *rc)
{ {
int retval = -1; int retval = -1;
restconf_stream_data *sd; restconf_stream_data *sd = NULL;
cvec *cvv = NULL; cvec *cvv = NULL;
char *cn; char *cn;
char *subject = NULL; char *subject = NULL;
@ -380,6 +380,13 @@ restconf_http1_path_root(clicon_handle h,
goto done; goto done;
goto fail; goto fail;
} }
#if 1
/* XXX gives mem leak in multiple requests,
* but maybe the error is that sd is not freed.
*/
if (sd->sd_path != NULL)
free(sd->sd_path);
#endif
if ((sd->sd_path = restconf_uripath(rc->rc_h)) == NULL) if ((sd->sd_path = restconf_uripath(rc->rc_h)) == NULL)
goto done; // XXX SHOULDNT EXIT if no REQUEST_URI goto done; // XXX SHOULDNT EXIT if no REQUEST_URI
if (rc->rc_proto_d2 == 0 && rc->rc_proto == HTTP_11) if (rc->rc_proto_d2 == 0 && rc->rc_proto == HTTP_11)
@ -427,7 +434,7 @@ restconf_http1_path_root(clicon_handle h,
if (api_root_restconf(h, sd, sd->sd_qvec) < 0) if (api_root_restconf(h, sd, sd->sd_qvec) < 0)
goto done; goto done;
} }
else if (api_path_is_data(h, NULL)){ else if (api_path_is_data(h)){
if (api_http_data(h, sd, sd->sd_qvec) < 0) if (api_http_data(h, sd, sd->sd_qvec) < 0)
goto done; goto done;
} }

View file

@ -325,7 +325,7 @@ restconf_nghttp2_path(restconf_stream_data *sd)
if (api_root_restconf(h, sd, sd->sd_qvec) < 0) if (api_root_restconf(h, sd, sd->sd_qvec) < 0)
goto done; goto done;
} }
else if (api_path_is_data(h, NULL)){ else if (api_path_is_data(h)){
if (api_http_data(h, sd, sd->sd_qvec) < 0) if (api_http_data(h, sd, sd->sd_qvec) < 0)
goto done; goto done;
} }
@ -475,14 +475,17 @@ http2_exec(restconf_conn *rc,
sd->sd_proto = HTTP_2; /* XXX is this necessary? */ sd->sd_proto = HTTP_2; /* XXX is this necessary? */
if (strncmp(sd->sd_path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0 if (strncmp(sd->sd_path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0
|| strcmp(sd->sd_path, RESTCONF_WELL_KNOWN) == 0 || strcmp(sd->sd_path, RESTCONF_WELL_KNOWN) == 0
|| api_path_is_data(rc->rc_h, NULL)){ || api_path_is_data(rc->rc_h)){
if (restconf_nghttp2_path(sd) < 0) if (restconf_nghttp2_path(sd) < 0)
goto done; goto done;
} }
else{ else{
sd->sd_code = 400; sd->sd_code = 404; /* not found */
; /* ignore */
} }
if (restconf_param_del_all(rc->rc_h) < 0) // XXX
goto done;
/* If body, add a content-length header /* If body, add a content-length header
* A server MUST NOT send a Content-Length header field in any response * A server MUST NOT send a Content-Length header field in any response
* with a status code of 1xx (Informational) or 204 (No Content). A * with a status code of 1xx (Informational) or 204 (No Content). A

View file

@ -78,18 +78,23 @@
int int
api_path_is_restconf(clicon_handle h) api_path_is_restconf(clicon_handle h)
{ {
char *path; int retval = 0;
char *path = NULL;
char *restconf_path = RESTCONF_API; char *restconf_path = RESTCONF_API;
if ((path = restconf_uripath(h)) == NULL) if ((path = restconf_uripath(h)) == NULL)
return 0; goto done;
if (strlen(path) < 1 + strlen(restconf_path)) /* "/" + restconf */ if (strlen(path) < 1 + strlen(restconf_path)) /* "/" + restconf */
return 0; goto done;
if (path[0] != '/') if (path[0] != '/')
return 0; goto done;
if (strncmp(path+1, restconf_path, strlen(restconf_path)) != 0) if (strncmp(path+1, restconf_path, strlen(restconf_path)) != 0)
return 0; goto done;
return 1; retval = 1;
done:
if (path)
free(path);
return retval;
} }
/*! Determine the root of the RESTCONF API by accessing /.well-known /*! Determine the root of the RESTCONF API by accessing /.well-known

View file

@ -126,20 +126,26 @@ static struct stream_child *STREAM_CHILD = NULL;
int int
api_path_is_stream(clicon_handle h) api_path_is_stream(clicon_handle h)
{ {
char *path; int retval = 0;
char *path = NULL;
char *stream_path; char *stream_path;
if ((path = restconf_uripath(h)) == NULL) if ((path = restconf_uripath(h)) == NULL)
return 0; goto done;
if ((stream_path = clicon_option_str(h, "CLICON_STREAM_PATH")) == NULL) if ((stream_path = clicon_option_str(h, "CLICON_STREAM_PATH")) == NULL)
return 0; goto done;
if (strlen(path) < 1 + strlen(stream_path)) /* "/" + stream */ if (strlen(path) < 1 + strlen(stream_path)) /* "/" + stream */
return 0; goto done;
if (path[0] != '/') if (path[0] != '/')
return 0; goto done;
if (strncmp(path+1, stream_path, strlen(stream_path)) != 0) if (strncmp(path+1, stream_path, strlen(stream_path)) != 0)
return 0; goto done;
return 1; retval = 1;
done:
if (path)
free(path);
return retval;
} }
/*! Find restconf child using PID and cleanup FCGI Request data /*! Find restconf child using PID and cleanup FCGI Request data

View file

@ -86,6 +86,7 @@ AC_DEFINE_UNQUOTED(CLIXON_VERSION_PATCH, $CLIXON_VERSION_PATCH, [Clixon path ver
AC_CHECK_LIB(m, main) AC_CHECK_LIB(m, main)
# defines: target_cpu, target_vendor, and target_os.
AC_CANONICAL_TARGET AC_CANONICAL_TARGET
# AC_SUBST(var) makes @var@ appear in makefiles. # AC_SUBST(var) makes @var@ appear in makefiles.

View file

@ -113,6 +113,16 @@ Use:
- PRIu64 for uint64 - PRIu64 for uint64
- %p for pointers - %p for pointers
### Include files
Avoid include statements in .h files, place them in .c files whenever possible.
The reason is to avoid deep include chains where file dependencies are
difficult to analyze and understand. If include statements are only placed in .c
files, there is only a single level of include file dependencies.
The drawback is that the same include file may need to be repeated in many .c files.
## How to work in git ## How to work in git
Clixon uses semantic versioning (https://semver.org). Clixon uses semantic versioning (https://semver.org).

View file

@ -94,6 +94,17 @@ DEFAULTNS="$DEFAULTONLY message-id=\"42\""
# Minimal hello message as a prelude to netconf rpcs # Minimal hello message as a prelude to netconf rpcs
DEFAULTHELLO="<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello $DEFAULTNS><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>" DEFAULTHELLO="<?xml version=\"1.0\" encoding=\"UTF-8\"?><hello $DEFAULTNS><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability><capability>urn:ietf:params:netconf:base:1.1</capability></capabilities></hello>]]>]]>"
# XXX cannot get this to work for all combinations of nc/netcat fcgi/native
# But leave it here for debugging where netcat works properly
if [ -n "$(type netcat 2> /dev/null)" ]; then
netcat="netcat -w 1" # -N does not work on fcgi
# nc on freebsd does not work either
#elif [ -n "$(type nc 2> /dev/null)" ]; then
# netcat=nc
else
netcat=
fi
# Options passed to curl calls # Options passed to curl calls
# -s : silent # -s : silent
# -S : show error # -S : show error

View file

@ -14,6 +14,7 @@ APPNAME=example
cfg=$dir/conf.xml cfg=$dir/conf.xml
rm -rf $dir/www rm -rf $dir/www
mkdir $dir/www mkdir $dir/www
mkdir $dir/www/data
# Does not work with fcgi # Does not work with fcgi
if [ "${WITH_RESTCONF}" = "fcgi" ]; then if [ "${WITH_RESTCONF}" = "fcgi" ]; then
@ -22,7 +23,7 @@ if [ "${WITH_RESTCONF}" = "fcgi" ]; then
fi fi
# Data file # Data file
cat <<EOF > $dir/www/index.html cat <<EOF > $dir/www/data/index.html
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
@ -43,7 +44,7 @@ working. Further configuration is required.</p>
</html> </html>
EOF EOF
cat <<EOF > $dir/www/example.css cat <<EOF > $dir/www/data/example.css
img { img {
display: inline; display: inline;
border: border:
@ -67,6 +68,54 @@ h1,h2,h3,h4,h5,h6 {
} }
EOF EOF
# Outside wwwdir, should not be able to access this
cat <<EOF > $dir/outside.html
<!DOCTYPE html>
<html>
<head>
<title>Dont access this</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Dont access this!</h1>
<p>If you see this page, you accessed a file outside the root domain</p>
</body>
</html>
EOF
# Create a soft link from inside to outside
ln -s $dir/outside.html $dir/www/data/inside.html
# Disable read access
cat <<EOF > $dir/www/data/noread.html
<!DOCTYPE html>
<html>
<head>
<title>No read</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>No read!</h1>
<p>If you see this page, you have read access to root</p>
</body>
</html>
EOF
# remove read access
chmod 660 $dir/www/data/noread.html
# Http test routine with arguments: # Http test routine with arguments:
# 1. proto:http/https # 1. proto:http/https
function testrun() function testrun()
@ -134,32 +183,55 @@ EOF
new "wait restconf" new "wait restconf"
wait_restconf $proto wait_restconf $proto
# echo "curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html" echo "curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html"
if $enable; then
if ! $enable; then
# XXX or bad request?
new "WWW get html, not enabled, expect not found"
# echo "curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html)" 0 "HTTP/$HVER 404"
else
new "WWW get html" new "WWW get html"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html)" 0 "HTTP/$HVER 200" "Content-Type: text/html" "<title>Welcome to Clixon!</title>" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html)" 0 "HTTP/$HVER 200" "Content-Type: text/html" "<title>Welcome to Clixon!</title>"
else
new "WWW get html, not enabled, expect bad request"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/index.html)" 0 "HTTP/$HVER 400"
return
fi
new "WWW get css" new "WWW get css"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/example.css)" 0 "HTTP/$HVER 200" "Content-Type: text/css" "display: inline;" --not-- "Content-Type: text/html" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/example.css)" 0 "HTTP/$HVER 200" "Content-Type: text/css" "display: inline;" --not-- "Content-Type: text/html"
new "WWW head" new "WWW head"
expectpart "$(curl $CURLOPTS --head -H 'Accept: text/html' $proto://localhost/data/index.html)" 0 "HTTP/$HVER 200" "Content-Type: text/html" --not-- "<title>Welcome to Clixon!</title>" expectpart "$(curl $CURLOPTS --head -H 'Accept: text/html' $proto://localhost/data/index.html)" 0 "HTTP/$HVER 200" "Content-Type: text/html" --not-- "<title>Welcome to Clixon!</title>"
new "WWW options" new "WWW options"
expectpart "$(curl $CURLOPTS -X OPTIONS $proto://localhost/data/index.html)" 0 "HTTP/$HVER 200" "allow: OPTIONS,HEAD,GET" expectpart "$(curl $CURLOPTS -X OPTIONS $proto://localhost/data/index.html)" 0 "HTTP/$HVER 200" "allow: OPTIONS,HEAD,GET"
# negative errors # negative errors
new "WWW get http not found" new "WWW get http not found"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/notfound.html)" 0 "HTTP/$HVER 404" "Content-Type: text/html" "<title>404 Not Found</title>" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/notfound.html)" 0 "HTTP/$HVER 404" "Content-Type: text/html" "<title>404 Not Found</title>"
new "WWW post not allowed" new "WWW get http soft link"
expectpart "$(curl $CURLOPTS -X POST -H 'Accept: text/html' -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $proto://localhost/data/notfound.html)" 0 "HTTP/$HVER 405" "Content-Type: text/html" "<title>405 Method Not Allowed</title>" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/inside.html)" 0 "HTTP/$HVER 403" "Content-Type: text/html" "<title>403 Forbidden</title>" --not-- "<title>Dont access this</title>"
if [ ! -f /.dockerenv ] ; then # XXX Privs dont not work on docker/alpine?
new "WWW get http not read access"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: text/html' $proto://localhost/data/noread.html)" 0 "HTTP/$HVER 403" "Content-Type: text/html" "<title>403 Forbidden</title>"
fi
# Try .. Cannot get .. in path to work in curl (it seems to remove it)
if [ "$proto" = http -a -n "$netcat" ]; then
new "WWW get outside using .. netcat"
expectpart "$(${netcat} 127.0.0.1 80 <<EOF
GET /data/../../outside.html HTTP/1.1
Host: localhost
Accept: text_html
EOF
)" 0 "HTTP/1.1 403" "Forbidden"
fi
new "WWW post not allowed"
expectpart "$(curl $CURLOPTS -X POST -H 'Accept: text/html' -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interfaces":{"interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}}' $proto://localhost/data/notfound.html)" 0 "HTTP/$HVER 405" "Content-Type: text/html" "<title>405 Method Not Allowed</title>"
fi
if [ $RC -ne 0 ]; then if [ $RC -ne 0 ]; then
new "Kill restconf daemon" new "Kill restconf daemon"
stop_restconf stop_restconf
@ -190,7 +262,7 @@ if [ "${WITH_RESTCONF}" = "native" ]; then
fi fi
for proto in $protos; do for proto in $protos; do
for enable in true false; do for enable in true false; do # false
new "http-data proto:$proto enabled:$enable" new "http-data proto:$proto enabled:$enable"
testrun $proto $enable testrun $proto $enable
done done

View file

@ -209,19 +209,6 @@ expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml
new "restconf GET initial datastore" new "restconf GET initial datastore"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 "HTTP/$HVER 200" "$XML" expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:a=0)" 0 "HTTP/$HVER 200" "$XML"
# XXX cannot get this to work for all combinations of nc/netcat fcgi/native
# But leave it here for debugging where netcat works properly
# Alt try something like:
# printf "Hello World!" | (exec 3<>/dev/tcp/127.0.0.1/80; cat >&3; cat <&3; exec 3<&-)
# Look for netcat or nc for direct socket http calls
if [ -n "$(type netcat 2> /dev/null)" ]; then
netcat="netcat -w 1" # -N does not work on fcgi
# nc on freebsd does not work either
#elif [ -n "$(type nc 2> /dev/null)" ]; then
# netcat=nc
else
netcat=
fi
if [ -n "$netcat" -a "${WITH_RESTCONF}" != "fcgi" ]; then if [ -n "$netcat" -a "${WITH_RESTCONF}" != "fcgi" ]; then
# new "restconf try fuzz crash" # new "restconf try fuzz crash"

View file

@ -605,16 +605,28 @@ module clixon-config {
} }
leaf CLICON_HTTP_DATA_PATH { leaf CLICON_HTTP_DATA_PATH {
if-feature "clrc:http-data"; if-feature "clrc:http-data";
default "/";
type string; type string;
description description
"If set, enable www data on this sub-path, must start with / (example: /data)"; "URI match for http-data serving files specified by CLICON_HTTP_DATA_ROOT.
Must start with / (example: /)
Restconf paths at /restconf is always done before data (or streams)
The PATH is appended to CLICON_HTTP_DATA_ROOT to find a file.
Example, if PATH is /data and ROOT is /www, and a GET /index.html, the
corresponding file is '/www/data/index.html'
Both feature clixon-restconf:http-data and restconf/enable-http-data
must be enabled for this match to occur.";
} }
leaf CLICON_HTTP_DATA_ROOT { leaf CLICON_HTTP_DATA_ROOT {
if-feature "clrc:http-data"; if-feature "clrc:http-data";
type string; type string;
default "/var/www"; default "/var/www";
description description
"public web root"; "Location in file system where http-data files are looked for.
Soft links, '..', '~' etc are not followed.
See also CLICON_HTTP_DATA_PATH
Both feature clixon-restconf:http-data and restconf/enable-http-data
must be enabled for this match to occur.";
} }
leaf CLICON_CLI_DIR { leaf CLICON_CLI_DIR {
type string; type string;
@ -1051,8 +1063,9 @@ module clixon-config {
leaf CLICON_STREAM_PATH { leaf CLICON_STREAM_PATH {
type string; type string;
default "streams"; default "streams";
description "Stream path appended to CLICON_STREAM_URL to form description
stream subscription URL."; "Stream path appended to CLICON_STREAM_URL to form
stream subscription URL.";
} }
leaf CLICON_STREAM_URL { leaf CLICON_STREAM_URL {
type string; type string;