/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2023 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "./u_login.h" #include "aqhome/http/httpservice.h" #include "aqhome/http/httpservice_http.h" #include "aqhome/http/httpservice_conf.h" #include #include #include #include #include #include #include static int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); static int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); static int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); static int _loginUser(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); static int _verifyPass(AQH_USER *u, const char *userName, const char *password); static AQH_SESSION *_generateSessionForUser(AQH_SERVICE *sv, AQH_USER *u); static GWEN_BUFFER *_generateSessionUid(void); static void _headerSetCookie(GWEN_DB_NODE *db, uint32_t flags, const char *cookieName, const char *cookieValue); static GWEN_MSG *_createLoginResponseMsg(AQH_SERVICE *sv, const char *protocol, const char *sessionId, const char *newPage); AQH_HTTP_URLHANDLER *AQH_LoginHttpUrlHandler_new(AQH_SERVICE *sv) { AQH_HTTP_URLHANDLER *uh; uh=AQH_HttpUrlHandler_new(sv); AQH_HttpUrlHandler_SetHandleFn(uh, _handleUrl); return uh; } int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { const char *protocol; const char *cmd; GWEN_MSG *msgOut=NULL; protocol=AQH_HttpRequest_GetProtocol(rq); cmd=AQH_HttpRequest_GetCommand(rq); if (cmd && *cmd) { int rv; if (strcasecmp(cmd, "GET")==0) { rv=_handleGet(uh, rq); if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); return rv; } } else if (strcasecmp(cmd, "POST")==0) { rv=_handlePost(uh, rq); if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); return rv; } } else { msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 405, "Method not allowed", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); } return 0; } else { DBG_ERROR(AQH_LOGDOMAIN, "No command in request"); return GWEN_ERROR_INVALID; } } int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { GWEN_BUFFER *pageBuf; int rv; GWEN_MSG *msgOut=NULL; const char *protocol; protocol=AQH_HttpRequest_GetProtocol(rq); pageBuf=GWEN_Buffer_new(0, 256, 0, 1); rv=AQH_HttpUrlHandler_AddContentHeaders(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf); if (rv<0) { DBG_ERROR(AQH_LOGDOMAIN, "Error adding headers"); GWEN_Buffer_free(pageBuf); msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } rv=AQH_HttpService_AddFile(AQH_HttpUrlHandler_GetHttpService(uh), NULL, "login.html", pageBuf); if (rv<0) { DBG_ERROR(AQH_LOGDOMAIN, "Error reading file \"login.html\""); GWEN_Buffer_free(pageBuf); msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } rv=AQH_HttpUrlHandler_AddContentFooters(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf); if (rv<0) { DBG_ERROR(AQH_LOGDOMAIN, "Error adding footers"); msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); GWEN_Buffer_free(pageBuf); return rv; } msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 200, "OK", protocol, GWEN_Buffer_GetStart(pageBuf)); GWEN_Buffer_free(pageBuf); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { GWEN_DB_NODE *db; int rv; DBG_ERROR(NULL, "Login POST:"); db=AQH_HttpRequest_GetDbPostBody(rq); GWEN_DB_Dump(db, 2); rv=_loginUser(uh, rq); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } return 0; } int _loginUser(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { AQH_SERVICE *sv; GWEN_MSG *msgOut=NULL; const char *protocol; GWEN_DB_NODE *db; const char *userName; const char *password; AQH_USER *u; AQH_SESSION *session; int i; int rv; sv=AQH_HttpUrlHandler_GetHttpService(uh); db=AQH_HttpRequest_GetDbPostBody(rq); protocol=AQH_HttpRequest_GetProtocol(rq); userName=GWEN_DB_GetCharValue(db, "username", 0, NULL); password=GWEN_DB_GetCharValue(db, "password", 0, NULL); if (!(userName && *userName && password && *password)) { msgOut=AQH_HttpService_CreateResponseMsg(sv, 401, "Unauthorized", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } u=AQH_HttpService_GetUser(sv, userName); if (u==NULL) { DBG_INFO(NULL, "User \"%s\" not found", userName); msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 401, "Unauthorized", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } i=AQH_User_GetState(u); if (i==AQH_UserState_Suspended) { DBG_INFO(NULL, "User \"%s\" suspended", userName); msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 401, "Unauthorized", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } rv=_verifyPass(u, userName, password); if (rv<0) { if (rv==GWEN_ERROR_VERIFY) msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 401, "Unauthorized", protocol, "error"); else msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } session=_generateSessionForUser(sv, u); if (session==NULL) { msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } DBG_ERROR(NULL, "Session generated"); AQH_User_SetTimestampLastLogin(u, AQH_Session_GetTimestampCreation(session)); rv=AQH_HttpService_WriteUser(sv, u); if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } msgOut=_createLoginResponseMsg(sv, protocol, AQH_Session_GetUid(session), "/rooms/list"); AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } int _verifyPass(AQH_USER *u, const char *userName, const char *password) { GWEN_MDIGEST *md; int rv; const char *storedPasswordHash; char buffer[70]; storedPasswordHash=AQH_User_GetHashedPassword(u); if (!(storedPasswordHash && *storedPasswordHash)) { DBG_ERROR(NULL, "No password hash stored with user \"%s\"", userName); return GWEN_ERROR_INTERNAL; } md=GWEN_MDigest_Sha256_new(); rv=GWEN_MDigest_Begin(md); if (rv<0) { DBG_ERROR(NULL, "Error digesting given password [begin] (%d)", rv); GWEN_MDigest_free(md); return rv; } rv=GWEN_MDigest_Update(md, (const uint8_t*) password, strlen(password)); if (rv<0) { DBG_ERROR(NULL, "Error digesting given password [update] (%d)", rv); GWEN_MDigest_free(md); return rv; } rv=GWEN_MDigest_End(md); if (rv<0) { DBG_ERROR(NULL, "Error digesting given password [end] (%d)", rv); GWEN_MDigest_free(md); return rv; } DBG_ERROR(NULL, "Digest needs %d bytes", (GWEN_MDigest_GetDigestSize(md)*2)+1); if (NULL==GWEN_Text_ToHex((const char*) GWEN_MDigest_GetDigestPtr(md), GWEN_MDigest_GetDigestSize(md), buffer, sizeof(buffer)-1)) { DBG_ERROR(NULL, "Buffer too small (need %d)", (GWEN_MDigest_GetDigestSize(md)*2)+1); GWEN_MDigest_free(md); return GWEN_ERROR_INTERNAL; } GWEN_MDigest_free(md); if (strcasecmp(buffer, storedPasswordHash)!=0) { DBG_ERROR(NULL, "Bad password for user \"%s\"", userName); return GWEN_ERROR_VERIFY; } DBG_ERROR(NULL, "Valid password for user \"%s\"", userName); return 0; } AQH_SESSION *_generateSessionForUser(AQH_SERVICE *sv, AQH_USER *u) { AQH_SESSION *session; GWEN_BUFFER *buf; GWEN_TIMESTAMP *ts; int rv; buf=_generateSessionUid(); if (buf==NULL) { DBG_INFO(NULL, "here"); return NULL; } DBG_INFO(NULL, "New session id: [%s]", GWEN_Buffer_GetStart(buf)); session=AQH_Session_new(); AQH_Session_SetUid(session, GWEN_Buffer_GetStart(buf)); ts=GWEN_Timestamp_NowInGmTime(); AQH_Session_SetTimestampCreation(session, ts); GWEN_Timestamp_free(ts); AQH_Session_SetUserAlias(session, AQH_User_GetAlias(u)); AQH_Session_SetUser(session, u); rv=AQH_HttpService_AddSession(sv, session); if (rv<0) { DBG_INFO(NULL, "Error adding session \"%s\" (%d)", AQH_Session_GetUid(session), rv); AQH_Session_free(session); return NULL; } return session; } GWEN_BUFFER *_generateSessionUid(void) { uint8_t binbuf[8]; GWEN_BUFFER *buf; int rv; rv=GWEN_SyncIo_Helper_PartiallyReadFile("/dev/urandom", binbuf, sizeof(binbuf)); if (rv