Преглед изворни кода

Added digest authentication methods. Fixed other validation masks as well.
Changed the request key for the table that holds info about the request from "data" to "info"

mingodad пре 13 година
родитељ
комит
5180d77d88
1 измењених фајлова са 274 додато и 10 уклоњено
  1. 274 10
      ext/sqmongoose.cpp

+ 274 - 10
ext/sqmongoose.cpp

@@ -262,6 +262,264 @@ sq_http_request_get_option(HSQUIRRELVM v)
     return 1;
     return 1;
 }
 }
 
 
+//Digest authentication functions
+#define MAX_USER_LEN  20
+#define MAX_NONCE_LEN  36 //extra bytes for zero terminator
+#define MAX_SESSIONS 5
+#define SESSION_TTL 3600
+#define SESSION_REQUEST_TTL 120
+
+enum e_session_state {e_session_invalid, e_session_sent_request, e_session_authorized};
+
+struct st_session_t
+{
+    int state;
+    char nonce[MAX_NONCE_LEN];
+    char user[MAX_USER_LEN];         // Authenticated user
+    long last_nc;
+    time_t last_access;       // last_access time
+    long remote_ip;           // Client's IP address
+};
+
+static struct st_session_t sessions[MAX_SESSIONS];  // Current sessions
+
+// Protects messages, sessions, last_message_id
+static mg_thread_mutex_t session_rwlock;
+
+//should be called only after locking session_rwlock
+static struct st_session_t *my_get_session_only_after_lock(const char *nonce){
+    int i;
+    for(i=0; i < MAX_SESSIONS; ++i){
+        //printf("%s : %s\n", sessions[i].nonce, ah->nonce);
+        if(strcmp(sessions[i].nonce, nonce) == 0){
+            return &sessions[i];
+        }
+    }
+    return 0;
+}
+
+// Check the user's password, return 1 if OK
+static SQBool my_check_password(const struct mg_request_info *request_info, const char *my_tmp_password)
+{
+    md5_buf_t ha2, expected_response;
+    struct mg_auth_header *ah = request_info->ah;
+
+    // Some of the parameters may be NULL
+    if (request_info->request_method == NULL ||
+            //strcmp(ri->request_method, "PUT") == 0 ||
+            //strcmp(ri->request_method, "DELETE") == 0 ||
+            ah == NULL ||
+            ah->nonce == NULL || ah->nc == NULL ||
+            ah->cnonce == NULL || ah->qop == NULL || ah->uri == NULL ||
+            ah->response == NULL)
+    {
+        return SQFalse;
+    }
+
+    // NOTE(lsm): due to a bug in MSIE, we do not compare the URI
+    // TODO(lsm): check for authentication timeout
+    if (// strcmp(ah->uri, conn->request_info.uri) != 0 ||
+        strlen(ah->response) != 32
+    )
+    {
+        return SQFalse;
+    }
+
+    mg_md5(ha2, request_info->request_method, ":", ah->uri, NULL);
+    mg_md5(expected_response, my_tmp_password /*ah->ha1*/, ":", ah->nonce, ":", ah->nc,
+           ":", ah->cnonce, ":", ah->qop, ":", ha2, NULL);
+    SQBool result = mg_strcasecmp(ah->response, expected_response) == 0;
+    //printf("%s : %s : %s\n", my_tmp_password, ah->response, expected_response);
+
+    if(result)  //lets check timeout and other issues
+    {
+        struct st_session_t *session_found = 0;
+        result = SQFalse;
+
+        mg_thread_mutex_lock(&session_rwlock);
+
+        session_found = my_get_session_only_after_lock(ah->nonce);
+        if(session_found)
+        {
+            do
+            {
+                //mobile ip is a pain for security
+                if( /*(ses.remote_ip != request_info->remote_ip) ||*/
+                        ((time(NULL) - session_found->last_access) > SESSION_TTL)
+                  ) break;
+                if(session_found->state == e_session_sent_request)
+                {
+                    session_found->state = e_session_authorized;
+                    snprintf(session_found->user, sizeof(session_found->user), "%s", ah->user);
+                }
+                else if(strcmp(session_found->user, ah->user) != 0) break;
+
+                long recived_nc = strtol(ah->nc, 0, 16);
+                time_t received_time = time(NULL);
+/*
+//printf("%d : %lu : %lu : %lu : %lu\n", request_info->remote_port, ses.last_nc, recived_nc, ses.last_access, received_time);
+                if((ses.last_nc+1) != recived_nc){
+                    //lets see if we can manage out of order sent by some browsers
+                    if((received_time - ses.last_access) > 2) break;
+                    //inside a window of 2 seconds we tolerate nc out of order
+                    if(ses.last_nc > recived_nc) recived_nc = ses.last_nc;
+                }
+*/
+                session_found->last_access = received_time;
+                session_found->last_nc = recived_nc;
+                result = SQTrue;
+            } while(0);
+
+            if(!result){
+                session_found->state = e_session_invalid;
+                session_found->nonce[0] = '\0';
+            }
+       }
+       else {
+           //dbg_msg("Session not found ! : %s : %s\n", ah->nonce, request_info->uri);
+       }
+
+        mg_thread_mutex_unlock(&session_rwlock);
+    }
+
+    return result;
+}
+
+static SQRESULT
+sq_http_request_check_password(HSQUIRRELVM v)
+{
+    SQ_FUNC_VARS_NO_TOP(v);
+    GET_http_request_INSTANCE();
+    SQ_GET_STRING(v, 2, my_tmp_password);
+    const struct mg_request_info *request_info = mg_get_request_info(conn);
+    sq_pushbool(v, my_check_password(request_info, my_tmp_password));
+    return 1;
+}
+
+// Close user session return 1 if closed
+static SQBool my_close_session(const struct mg_request_info *request_info)
+{
+    SQBool result = SQFalse;
+    struct st_session_t *session_found = 0;
+    struct mg_auth_header *ah = request_info->ah;
+
+    // Some of the parameters may be NULL
+    if (request_info->request_method == NULL ||
+            //strcmp(ri->request_method, "PUT") == 0 ||
+            //strcmp(ri->request_method, "DELETE") == 0 ||
+            ah == NULL ||
+            ah->nonce == NULL || ah->nc == NULL ||
+            ah->cnonce == NULL || ah->qop == NULL || ah->uri == NULL ||
+            ah->response == NULL)
+    {
+        return 0;
+    }
+
+    mg_thread_mutex_lock(&session_rwlock);
+
+    session_found = my_get_session_only_after_lock(ah->nonce);
+    if(session_found) {
+        session_found->state = e_session_invalid;
+        session_found->nonce[0] = '\0';
+        result = SQTrue;
+    }
+    mg_thread_mutex_unlock(&session_rwlock);
+    return result;
+}
+
+static SQRESULT
+sq_http_request_close_session(HSQUIRRELVM v)
+{
+    SQ_FUNC_VARS_NO_TOP(v);
+    GET_http_request_INSTANCE();
+    const struct mg_request_info *request_info = mg_get_request_info(conn);
+    sq_pushbool(v, my_close_session(request_info));
+    return 1;
+}
+
+typedef char buf_1024_t[1024];
+
+static const char *my_encode_nonce(buf_1024_t buf, md5_buf_t md5_buf, unsigned long ip)
+{
+    snprintf(buf, sizeof(buf), "%lu:%d:%d:%lu", (unsigned long) time(NULL), rand(), rand(), ip);
+    mg_md5(md5_buf, buf, NULL);
+    return md5_buf;
+}
+
+static void my_send_authorization_request(struct mg_connection *conn,
+        const struct mg_request_info *request_info,
+        const char *authentication_domain,
+        const char *nonce)
+{
+    md5_buf_t md5_buf;
+    buf_1024_t buf;
+    int i;
+    if (nonce == NULL)
+    {
+        nonce = my_encode_nonce(buf, md5_buf, (unsigned long) request_info->remote_ip);
+    }
+    struct st_session_t *available_session = NULL;
+
+    mg_thread_mutex_lock(&session_rwlock);
+
+    for(i=0; i<3; ++i)
+    {
+        for(i=0; i < MAX_SESSIONS; ++i){
+            if(sessions[i].state == e_session_invalid){
+                available_session = &sessions[i];
+            }
+            if(sessions[i].state == e_session_sent_request){
+                if((time(NULL) - available_session->last_access) > SESSION_REQUEST_TTL){
+                    //if session request bigger than 2 minutes reuse it
+                    available_session = &sessions[i];
+                }
+            }
+        }
+
+        if(available_session){
+            available_session->state = e_session_sent_request;
+            available_session->last_access = time(NULL);
+            available_session->last_nc = 0;
+            available_session->remote_ip = request_info->remote_ip;
+            snprintf(available_session->nonce, sizeof(available_session->nonce),
+                     "%s", nonce);
+            break;
+        }
+        else
+        {
+            nonce = my_encode_nonce(buf, md5_buf, (unsigned long) request_info->remote_ip);
+        }
+    }
+
+    mg_thread_mutex_unlock(&session_rwlock);
+
+    if(available_session) {
+        i = snprintf(buf, sizeof(buf),
+            "HTTP/1.1 401 Unauthorized\r\n"
+            "Content-Length: 0\r\n"
+            "WWW-Authenticate: Digest qop=\"auth\", "
+            "realm=\"%s\", nonce=\"%s\"\r\n\r\n",
+            authentication_domain, nonce);
+    } else {
+        i = snprintf(buf, sizeof(buf),
+            "HTTP/1.1 503 Service Temporary Unavailable\r\n"
+            "Content-Length: 0\r\n\r\n");
+    }
+
+    mg_write(conn, buf, i);
+}
+
+static SQRESULT
+sq_http_request_send_authorization_request(HSQUIRRELVM v)
+{
+    SQ_FUNC_VARS_NO_TOP(v);
+    GET_http_request_INSTANCE();
+    SQ_GET_STRING(v, 2, authentication_domain);
+    const struct mg_request_info *request_info = mg_get_request_info(conn);
+    my_send_authorization_request(conn, request_info, authentication_domain, NULL);
+    return 0;
+}
+
 #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),  sq_http_request_##name,nparams,tycheck}
 #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),  sq_http_request_##name,nparams,tycheck}
 static SQRegFunction mg_http_request_methods[] =
 static SQRegFunction mg_http_request_methods[] =
 {
 {
@@ -271,12 +529,15 @@ static SQRegFunction mg_http_request_methods[] =
 	_DECL_FUNC(read,  2, _SC("xi")),
 	_DECL_FUNC(read,  2, _SC("xi")),
 	_DECL_FUNC(write,  3, _SC("xsi")),
 	_DECL_FUNC(write,  3, _SC("xsi")),
 	_DECL_FUNC(write_blob,  -2, _SC("xxi")),
 	_DECL_FUNC(write_blob,  -2, _SC("xxi")),
-	_DECL_FUNC(get_var,  2, _SC("xs")),
+	_DECL_FUNC(get_var,  3, _SC("xs")),
 	_DECL_FUNC(get_cookie,  2, _SC("xs")),
 	_DECL_FUNC(get_cookie,  2, _SC("xs")),
 	_DECL_FUNC(get_header,  2, _SC("xs")),
 	_DECL_FUNC(get_header,  2, _SC("xs")),
 	_DECL_FUNC(send_file,  2, _SC("xs")),
 	_DECL_FUNC(send_file,  2, _SC("xs")),
 	_DECL_FUNC(handle_cgi_request,  2, _SC("xs")),
 	_DECL_FUNC(handle_cgi_request,  2, _SC("xs")),
 	_DECL_FUNC(get_option,  2, _SC("xs")),
 	_DECL_FUNC(get_option,  2, _SC("xs")),
+	_DECL_FUNC(check_password,  2, _SC("xs")),
+	_DECL_FUNC(close_session,  1, _SC("x")),
+	_DECL_FUNC(send_authorization_request,  2, _SC("xs")),
 	{0,0}
 	{0,0}
 };
 };
 #undef _DECL_FUNC
 #undef _DECL_FUNC
@@ -628,9 +889,9 @@ static SQRegFunction sq_mg_methods[] =
 	_DECL_FUNC(url_decode,  2, _SC(".s")),
 	_DECL_FUNC(url_decode,  2, _SC(".s")),
 	_DECL_FUNC(uri_decode,  2, _SC(".s")),
 	_DECL_FUNC(uri_decode,  2, _SC(".s")),
 	_DECL_FUNC(url_encode,  2, _SC(".s")),
 	_DECL_FUNC(url_encode,  2, _SC(".s")),
-	_DECL_FUNC(md5,  -2, _SC("xs")),
+	_DECL_FUNC(md5,  -2, _SC(".s")),
 #ifdef JNI_ENABLE_LOG
 #ifdef JNI_ENABLE_LOG
-	_DECL_FUNC(jniLog,  -2, _SC("xs")),
+	_DECL_FUNC(jniLog,  -2, _SC(".s")),
 #endif
 #endif
 	_DECL_FUNC(debug_print,  -2, _SC(".s")),
 	_DECL_FUNC(debug_print,  -2, _SC(".s")),
 	{0,0}
 	{0,0}
@@ -656,7 +917,7 @@ push_request(HSQUIRRELVM v, const struct mg_request_info *ri)
 {
 {
     int i;
     int i;
 
 
-    sq_pushliteral(v, _SC("data"));
+    sq_pushliteral(v, _SC("info"));
     sq_get(v, -2);
     sq_get(v, -2);
 #define NEWSLOT_STR(ks) reg_string(v, #ks, ri->ks);
 #define NEWSLOT_STR(ks) reg_string(v, #ks, ri->ks);
     NEWSLOT_STR(request_method);
     NEWSLOT_STR(request_method);
@@ -776,6 +1037,7 @@ void sq_errorfunc(HSQUIRRELVM v,const SQChar *s,...)
 	va_end(vl);
 	va_end(vl);
 }
 }
 
 
+#define INT_CONST(v,num) 	sq_pushstring(v,_SC(#num),-1);sq_pushinteger(v,num);sq_newslot(v,-3,SQTrue);
 
 
 static HSQUIRRELVM my_new_squirrel(struct mg_context *ctx) {
 static HSQUIRRELVM my_new_squirrel(struct mg_context *ctx) {
     HSQUIRRELVM v = sq_open(1024);
     HSQUIRRELVM v = sq_open(1024);
@@ -801,7 +1063,7 @@ static HSQUIRRELVM my_new_squirrel(struct mg_context *ctx) {
     sq_newclass(v,SQFalse);
     sq_newclass(v,SQFalse);
     sq_settypetag(v,-1,(void*)sq_http_request_TAG);
     sq_settypetag(v,-1,(void*)sq_http_request_TAG);
     sq_insert_reg_funcs(v, mg_http_request_methods);
     sq_insert_reg_funcs(v, mg_http_request_methods);
-    sq_pushstring(v, _SC("data"), -1);
+    sq_pushstring(v, _SC("info"), -1);
     sq_newtable(v);
     sq_newtable(v);
     sq_newslot(v,-3,SQFalse);
     sq_newslot(v,-3,SQFalse);
     sq_newslot(v,-3,SQFalse);
     sq_newslot(v,-3,SQFalse);
@@ -949,12 +1211,14 @@ user_callback_proxy(enum mg_event event,
                 }
                 }
                 sq_pushroottable(v);
                 sq_pushroottable(v);
 
 
+#define CASE(n) case n: sq_pushstring(v, #n, -1);break
                 switch (event) {
                 switch (event) {
-                case MG_NEW_REQUEST:    sq_pushliteral(v, "MG_NEW_REQUEST");    break;
-                case MG_HTTP_ERROR:     sq_pushliteral(v, "MG_HTTP_ERROR");     break;
-                case MG_EVENT_LOG:      sq_pushliteral(v, "MG_EVENT_LOG");      break;
-                case MG_INIT_SSL:       sq_pushliteral(v, "MG_INIT_SSL");       break;
-                default:                sq_pushnull(v);                         break;
+                    CASE(MG_INIT_SSL);
+                    CASE(MG_HTTP_ERROR);
+                    CASE(MG_EVENT_LOG);
+                    CASE(MG_NEW_REQUEST);
+                    default:
+                        sq_pushnull(v);
                 }
                 }
 
 
                 sq_pushstring(v, sq_http_request_TAG, -1);
                 sq_pushstring(v, sq_http_request_TAG, -1);