diff --git a/include/CivetServer.h b/include/CivetServer.h index 6be01140..42c6c66b 100644 --- a/include/CivetServer.h +++ b/include/CivetServer.h @@ -97,6 +97,31 @@ class CIVETWEB_API CivetHandler virtual bool handlePatch(CivetServer *server, struct mg_connection *conn); }; +/** + * Basic interface for a URI authorization handler. Handler implementations + * must be reentrant. + */ +class CIVETWEB_API CivetAuthHandler +{ + public: + /** + * Destructor + */ + virtual ~CivetAuthHandler() + { + } + + /** + * Callback method for authorization requests. It is up the this handler + * to generate 401 responses if authorization fails. + * + * @param server - the calling server + * @param conn - the connection information + * @returns true if authorization succeeded, false otherwise + */ + virtual bool authorize(CivetServer *server, struct mg_connection *conn) = 0; +}; + /** * Basic interface for a websocket handler. Handlers implementations * must be reentrant. @@ -263,6 +288,34 @@ class CIVETWEB_API CivetServer */ void removeWebSocketHandler(const std::string &uri); + /** + * addAuthHandler(const std::string &, CivetAuthHandler *) + * + * Adds a URI authorization handler. If there is existing URI authorization + * handler, it will be replaced with this one. + * + * URI's are ordered and prefix (REST) URI's are supported. + * + * @param uri - URI to match. + * @param handler - authorization handler instance to use. + */ + void addAuthHandler(const std::string &uri, CivetAuthHandler *handler); + + void + addAuthHandler(const std::string &uri, CivetAuthHandler &handler) + { + addAuthHandler(uri, &handler); + } + + /** + * removeAuthHandler(const std::string &) + * + * Removes an authorization handler. + * + * @param uri - the exact URL used in addAuthHandler(). + */ + void removeAuthHandler(const std::string &uri); + /** * getListeningPorts() * @@ -490,6 +543,17 @@ class CIVETWEB_API CivetServer void *cbdata); static void webSocketCloseHandler(const struct mg_connection *conn, void *cbdata); + /** + * authHandler(struct mg_connection *, void *cbdata) + * + * Handles the authorization requests. + * + * @param conn - the connection information + * @param cbdata - pointer to the CivetAuthHandler instance. + * @returns 1 if authorized, 0 otherwise + */ + static int authHandler(struct mg_connection *conn, void *cbdata); + /** * closeHandler(struct mg_connection *) * diff --git a/include/civetweb.h b/include/civetweb.h index 7c81eb21..0acbc99d 100644 --- a/include/civetweb.h +++ b/include/civetweb.h @@ -334,6 +334,29 @@ mg_set_websocket_handler(struct mg_context *ctx, mg_websocket_close_handler close_handler, void *cbdata); +/* mg_authorization_handler + + Some description here + + Parameters: + conn: current connection information. + cbdata: the callback data configured with mg_set_request_handler(). + Returns: + 0: access denied + 1: access granted + */ +typedef int (*mg_authorization_handler)(struct mg_connection *conn, + void *cbdata); + +/* mg_set_auth_handler + + Sets or removes a URI mapping for an authorization handler. + This function works similar to mg_set_request_handler - see there. */ +CIVETWEB_API void mg_set_auth_handler(struct mg_context *ctx, + const char *uri, + mg_authorization_handler handler, + void *cbdata); + /* Get the value of particular configuration parameter. The value returned is read-only. Civetweb does not allow changing configuration at run time. diff --git a/src/CivetServer.cpp b/src/CivetServer.cpp index dc25326f..405c2889 100644 --- a/src/CivetServer.cpp +++ b/src/CivetServer.cpp @@ -142,6 +142,31 @@ CivetServer::requestHandler(struct mg_connection *conn, void *cbdata) return 0; // No handler found } +int +CivetServer::authHandler(struct mg_connection *conn, void *cbdata) +{ + const struct mg_request_info *request_info = mg_get_request_info(conn); + assert(request_info != NULL); + CivetServer *me = (CivetServer *)(request_info->user_data); + assert(me != NULL); + + // Happens when a request hits the server before the context is saved + if (me->context == NULL) + return 0; + + mg_lock_context(me->context); + me->connections[conn] = CivetConnection(); + mg_unlock_context(me->context); + + CivetAuthHandler *handler = (CivetAuthHandler *)cbdata; + + if (handler) { + return handler->authorize(me, conn) ? 1 : 0; + } + + return 0; // No handler found +} + int CivetServer::webSocketConnectionHandler(const struct mg_connection *conn, void *cbdata) @@ -263,7 +288,7 @@ CivetServer::CivetServer(std::vector options, callbacks.connection_close = closeHandler; std::vector pointers(options.size()); - for (int i = 0; i < options.size(); i++) { + for (size_t i = 0; i < options.size(); i++) { pointers.push_back(options[i].c_str()); } pointers.push_back(0); @@ -317,6 +342,12 @@ CivetServer::addWebSocketHandler(const std::string &uri, handler); } +void +CivetServer::addAuthHandler(const std::string &uri, CivetAuthHandler *handler) +{ + mg_set_auth_handler(context, uri.c_str(), authHandler, handler); +} + void CivetServer::removeHandler(const std::string &uri) { @@ -330,6 +361,12 @@ CivetServer::removeWebSocketHandler(const std::string &uri) context, uri.c_str(), NULL, NULL, NULL, NULL, NULL); } +void +CivetServer::removeAuthHandler(const std::string &uri) +{ + mg_set_auth_handler(context, uri.c_str(), NULL, NULL); +} + void CivetServer::close() { diff --git a/src/civetweb.c b/src/civetweb.c index a4dad1a5..881bc0ad 100755 --- a/src/civetweb.c +++ b/src/civetweb.c @@ -1159,16 +1159,17 @@ mg_static_assert((sizeof(config_options) / sizeof(config_options[0])) == (NUM_OPTIONS + 1), "config_options and enum not sync"); +enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER }; -struct mg_request_handler_info { +struct mg_handler_info { /* Name/Pattern of the URI. */ char *uri; size_t uri_len; - /* URI type: ws/wss (websocket) or http/https (web page). */ - int is_websocket_handler; + /* handler type */ + int handler_type; - /* Handler for http/https requests. */ + /* Handler for http/https or authorization requests. */ mg_request_handler handler; /* Handler for ws/wss (websocket) requests. */ @@ -1177,11 +1178,14 @@ struct mg_request_handler_info { mg_websocket_data_handler data_handler; mg_websocket_close_handler close_handler; + /* Handler for authorization requests */ + mg_authorization_handler auth_handler; + /* User supplied argument for the handler function. */ void *cbdata; - /* next request handler in a linked list */ - struct mg_request_handler_info *next; + /* next handler in a linked list */ + struct mg_handler_info *next; }; struct mg_context { @@ -1219,7 +1223,7 @@ struct mg_context { char *systemName; /* What operating system is running */ /* linked list of uri handlers */ - struct mg_request_handler_info *request_handlers; + struct mg_handler_info *handlers; #if defined(USE_LUA) && defined(USE_WEBSOCKET) /* linked list of shared lua websockets */ @@ -9099,26 +9103,28 @@ redirect_to_https_port(struct mg_connection *conn, int ssl_index) static void -mg_set_request_handler_type(struct mg_context *ctx, - const char *uri, - int is_websocket_handler, - int is_delete_request, - mg_request_handler handler, - mg_websocket_connect_handler connect_handler, - mg_websocket_ready_handler ready_handler, - mg_websocket_data_handler data_handler, - mg_websocket_close_handler close_handler, - void *cbdata) +mg_set_handler_type(struct mg_context *ctx, + const char *uri, + int handler_type, + int is_delete_request, + mg_request_handler handler, + mg_websocket_connect_handler connect_handler, + mg_websocket_ready_handler ready_handler, + mg_websocket_data_handler data_handler, + mg_websocket_close_handler close_handler, + mg_authorization_handler auth_handler, + void *cbdata) { - struct mg_request_handler_info *tmp_rh, **lastref; + struct mg_handler_info *tmp_rh, **lastref; size_t urilen = strlen(uri); - if (is_websocket_handler) { + if (handler_type == WEBSOCKET_HANDLER) { /* assert(handler == NULL); */ /* assert(is_delete_request || connect_handler!=NULL || * ready_handler!=NULL || data_handler!=NULL || * close_handler!=NULL); */ + /* assert(auth_handler == NULL); */ if (handler != NULL) { return; } @@ -9128,11 +9134,15 @@ mg_set_request_handler_type(struct mg_context *ctx, && close_handler == NULL) { return; } - } else { + if (auth_handler != NULL) { + return; + } + } else if (handler_type == REQUEST_HANDLER) { /* assert(connect_handler==NULL && ready_handler==NULL && * data_handler==NULL && close_handler==NULL); */ /* assert(is_delete_request || (handler!=NULL)); */ + /* assert(auth_handler == NULL); */ if (connect_handler != NULL || ready_handler != NULL || data_handler != NULL || close_handler != NULL) { @@ -9141,6 +9151,25 @@ mg_set_request_handler_type(struct mg_context *ctx, if (!is_delete_request && (handler == NULL)) { return; } + if (auth_handler != NULL) { + return; + } + } else { /* AUTH_HANDLER */ + /* assert(handler == NULL); */ + /* assert(connect_handler==NULL && ready_handler==NULL && + * data_handler==NULL && close_handler==NULL); */ + /* assert(auth_handler != NULL); */ + if (handler != NULL) { + return; + } + if (connect_handler != NULL || ready_handler != NULL + || data_handler != NULL + || close_handler != NULL) { + return; + } + if (!is_delete_request && (auth_handler == NULL)) { + return; + } } if (!ctx) { @@ -9150,20 +9179,21 @@ mg_set_request_handler_type(struct mg_context *ctx, mg_lock_context(ctx); /* first try to find an existing handler */ - lastref = &(ctx->request_handlers); - for (tmp_rh = ctx->request_handlers; tmp_rh != NULL; - tmp_rh = tmp_rh->next) { - if (tmp_rh->is_websocket_handler == is_websocket_handler) { + lastref = &(ctx->handlers); + for (tmp_rh = ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { + if (tmp_rh->handler_type == handler_type) { if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) { if (!is_delete_request) { /* update existing handler */ - if (!is_websocket_handler) { + if (handler_type == REQUEST_HANDLER) { tmp_rh->handler = handler; - } else { + } else if (handler_type == WEBSOCKET_HANDLER) { tmp_rh->connect_handler = connect_handler; tmp_rh->ready_handler = ready_handler; tmp_rh->data_handler = data_handler; tmp_rh->close_handler = close_handler; + } else { /* AUTH_HANDLER */ + tmp_rh->auth_handler = auth_handler; } tmp_rh->cbdata = cbdata; } else { @@ -9186,8 +9216,8 @@ mg_set_request_handler_type(struct mg_context *ctx, return; } - tmp_rh = (struct mg_request_handler_info *) - mg_calloc(sizeof(struct mg_request_handler_info), 1); + tmp_rh = + (struct mg_handler_info *)mg_calloc(sizeof(struct mg_handler_info), 1); if (tmp_rh == NULL) { mg_unlock_context(ctx); mg_cry(fc(ctx), "%s", "Cannot create new request handler struct, OOM"); @@ -9201,16 +9231,18 @@ mg_set_request_handler_type(struct mg_context *ctx, return; } tmp_rh->uri_len = urilen; - if (!is_websocket_handler) { + if (handler_type == REQUEST_HANDLER) { tmp_rh->handler = handler; - } else { + } else if (handler_type == WEBSOCKET_HANDLER) { tmp_rh->connect_handler = connect_handler; tmp_rh->ready_handler = ready_handler; tmp_rh->data_handler = data_handler; tmp_rh->close_handler = close_handler; + } else { /* AUTH_HANDLER */ + tmp_rh->auth_handler = auth_handler; } tmp_rh->cbdata = cbdata; - tmp_rh->is_websocket_handler = is_websocket_handler; + tmp_rh->handler_type = handler_type; tmp_rh->next = NULL; *lastref = tmp_rh; @@ -9224,8 +9256,17 @@ mg_set_request_handler(struct mg_context *ctx, mg_request_handler handler, void *cbdata) { - mg_set_request_handler_type( - ctx, uri, 0, handler == NULL, handler, NULL, NULL, NULL, NULL, cbdata); + mg_set_handler_type(ctx, + uri, + REQUEST_HANDLER, + handler == NULL, + handler, + NULL, + NULL, + NULL, + NULL, + NULL, + cbdata); } @@ -9241,34 +9282,54 @@ mg_set_websocket_handler(struct mg_context *ctx, int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL) && (data_handler == NULL) && (close_handler == NULL); - mg_set_request_handler_type(ctx, - uri, - 1, - is_delete_request, - NULL, - connect_handler, - ready_handler, - data_handler, - close_handler, - cbdata); + mg_set_handler_type(ctx, + uri, + WEBSOCKET_HANDLER, + is_delete_request, + NULL, + connect_handler, + ready_handler, + data_handler, + close_handler, + NULL, + cbdata); } +void +mg_set_auth_handler(struct mg_context *ctx, + const char *uri, + mg_request_handler handler, + void *cbdata) +{ + mg_set_handler_type(ctx, + uri, + AUTH_HANDLER, + handler == NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + handler, + cbdata); +} static int get_request_handler(struct mg_connection *conn, - int is_websocket_request, + int handler_type, mg_request_handler *handler, mg_websocket_connect_handler *connect_handler, mg_websocket_ready_handler *ready_handler, mg_websocket_data_handler *data_handler, mg_websocket_close_handler *close_handler, + mg_authorization_handler *auth_handler, void **cbdata) { const struct mg_request_info *request_info = mg_get_request_info(conn); if (request_info) { const char *uri = request_info->local_uri; size_t urilen = strlen(uri); - struct mg_request_handler_info *tmp_rh; + struct mg_handler_info *tmp_rh; if (!conn || !conn->ctx) { return 0; @@ -9277,17 +9338,19 @@ get_request_handler(struct mg_connection *conn, mg_lock_context(conn->ctx); /* first try for an exact match */ - for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL; + for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { - if (tmp_rh->is_websocket_handler == is_websocket_request) { + if (tmp_rh->handler_type == handler_type) { if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) { - if (is_websocket_request) { + if (handler_type == WEBSOCKET_HANDLER) { *connect_handler = tmp_rh->connect_handler; *ready_handler = tmp_rh->ready_handler; *data_handler = tmp_rh->data_handler; *close_handler = tmp_rh->close_handler; - } else { + } else if (handler_type == REQUEST_HANDLER) { *handler = tmp_rh->handler; + } else { /* AUTH_HANDLER */ + *auth_handler = tmp_rh->auth_handler; } *cbdata = tmp_rh->cbdata; mg_unlock_context(conn->ctx); @@ -9297,18 +9360,20 @@ get_request_handler(struct mg_connection *conn, } /* next try for a partial match, we will accept uri/something */ - for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL; + for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { - if (tmp_rh->is_websocket_handler == is_websocket_request) { + if (tmp_rh->handler_type == handler_type) { if (tmp_rh->uri_len < urilen && uri[tmp_rh->uri_len] == '/' && memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) { - if (is_websocket_request) { + if (handler_type == WEBSOCKET_HANDLER) { *connect_handler = tmp_rh->connect_handler; *ready_handler = tmp_rh->ready_handler; *data_handler = tmp_rh->data_handler; *close_handler = tmp_rh->close_handler; - } else { + } else if (handler_type == REQUEST_HANDLER) { *handler = tmp_rh->handler; + } else { /* AUTH_HANDLER */ + *auth_handler = tmp_rh->auth_handler; } *cbdata = tmp_rh->cbdata; mg_unlock_context(conn->ctx); @@ -9318,17 +9383,19 @@ get_request_handler(struct mg_connection *conn, } /* finally try for pattern match */ - for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL; + for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { - if (tmp_rh->is_websocket_handler == is_websocket_request) { + if (tmp_rh->handler_type == handler_type) { if (match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) { - if (is_websocket_request) { + if (handler_type == WEBSOCKET_HANDLER) { *connect_handler = tmp_rh->connect_handler; *ready_handler = tmp_rh->ready_handler; *data_handler = tmp_rh->data_handler; *close_handler = tmp_rh->close_handler; - } else { + } else if (handler_type == REQUEST_HANDLER) { *handler = tmp_rh->handler; + } else { /* AUTH_HANDLER */ + *auth_handler = tmp_rh->auth_handler; } *cbdata = tmp_rh->cbdata; mg_unlock_context(conn->ctx); @@ -9405,6 +9472,8 @@ handle_request(struct mg_connection *conn) mg_websocket_data_handler ws_data_handler = NULL; mg_websocket_close_handler ws_close_handler = NULL; void *callback_data = NULL; + mg_authorization_handler auth_handler = NULL; + void *auth_callback_data = NULL; #if !defined(NO_FILES) time_t curtime = time(NULL); char date[64]; @@ -9491,12 +9560,14 @@ handle_request(struct mg_connection *conn) /* 5.2. check if the request will be handled by a callback */ if (get_request_handler(conn, - is_websocket_request, + is_websocket_request ? WEBSOCKET_HANDLER + : REQUEST_HANDLER, &callback_handler, &ws_connect_handler, &ws_ready_handler, &ws_data_handler, &ws_close_handler, + NULL, &callback_data)) { /* 5.2.1. A callback will handle this request. All requests * handled @@ -9522,10 +9593,23 @@ handle_request(struct mg_connection *conn) } /* 6. authorization check */ - if (is_put_or_delete_request && !is_script_resource - && !is_callback_resource) { -/* 6.1. this request is a PUT/DELETE to a real file */ -/* 6.1.1. thus, the server must have real files */ + /* 6.1. a custom authorization handler is installed */ + if (get_request_handler(conn, + AUTH_HANDLER, + NULL, + NULL, + NULL, + NULL, + NULL, + &auth_handler, + &auth_callback_data)) { + if (!auth_handler(conn, auth_callback_data)) { + return; + } + } else if (is_put_or_delete_request && !is_script_resource + && !is_callback_resource) { +/* 6.2. this request is a PUT/DELETE to a real file */ +/* 6.2.1. thus, the server must have real files */ #if defined(NO_FILES) if (1) { #else @@ -9541,8 +9625,7 @@ handle_request(struct mg_connection *conn) } #if !defined(NO_FILES) - /* 6.1.2. Check if put authorization for static files is - * available. + /* 6.2.2. Check if put authorization for static files is available. */ if (!is_authorized_for_put(conn)) { send_authorization_request(conn); @@ -9551,7 +9634,7 @@ handle_request(struct mg_connection *conn) #endif } else { - /* 6.2. This is either a OPTIONS, GET, HEAD or POST request, + /* 6.3. This is either a OPTIONS, GET, HEAD or POST request, * or it is a PUT or DELETE request to a resource that does not * correspond to a file. Check authorization. */ if (!check_authorization(conn, path)) { @@ -12126,7 +12209,7 @@ static void free_context(struct mg_context *ctx) { int i; - struct mg_request_handler_info *tmp_rh; + struct mg_handler_info *tmp_rh; if (ctx == NULL) { return; @@ -12162,9 +12245,9 @@ free_context(struct mg_context *ctx) } /* Deallocate request handlers */ - while (ctx->request_handlers) { - tmp_rh = ctx->request_handlers; - ctx->request_handlers = tmp_rh->next; + while (ctx->handlers) { + tmp_rh = ctx->handlers; + ctx->handlers = tmp_rh->next; mg_free(tmp_rh->uri); mg_free(tmp_rh); } @@ -12359,7 +12442,7 @@ mg_start(const struct mg_callbacks *callbacks, ctx->callbacks.exit_context = 0; } ctx->user_data = user_data; - ctx->request_handlers = NULL; + ctx->handlers = NULL; #if defined(USE_LUA) && defined(USE_WEBSOCKET) ctx->shared_lua_websockets = 0;