X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=src%2Fserver%2Fserver.c;h=2bcd8d3e7e0c60f82cd4337c5bd934b32b750d30;hb=de8c5fd93c75b8611cba5530b255a3dcfd97db41;hp=4404f9e68bb06c0d0322c9d34683cd6102ffcb3a;hpb=4fef498619ca05876568c6bc5114fadd7c2496ee;p=tpg%2Fopendispense2.git diff --git a/src/server/server.c b/src/server/server.c index 4404f9e..2bcd8d3 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -14,12 +14,16 @@ #include #include #include +#include // O_* #include #include #include -#include +#include // Signal handling +#include // AUTHIDENT +#include // time(2) #define DEBUG_TRACE_CLIENT 0 +#define HACK_NO_REFUNDS 1 // Statistics #define MAX_CONNECTION_QUEUE 5 @@ -31,13 +35,17 @@ #define MSG_STR_TOO_LONG "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n" +#define IDENT_TRUSTED_NETWORK 0x825F0D00 +#define IDENT_TRUSTED_NETMASK 0xFFFFFFC0 + // === TYPES === typedef struct sClient { int Socket; // Client socket ID int ID; // Client ID - int bIsTrusted; // Is the connection from a trusted host/port + int bTrustedHost; + int bCanAutoAuth; // Is the connection from a trusted host/port char *Username; char Salt[9]; @@ -50,12 +58,13 @@ typedef struct sClient // === PROTOTYPES === void Server_Start(void); void Server_Cleanup(void); -void Server_HandleClient(int Socket, int bTrusted); +void Server_HandleClient(int Socket, int bTrustedHost, int bRootPort); void Server_ParseClientCommand(tClient *Client, char *CommandString); // --- Commands --- void Server_Cmd_USER(tClient *Client, char *Args); void Server_Cmd_PASS(tClient *Client, char *Args); void Server_Cmd_AUTOAUTH(tClient *Client, char *Args); +void Server_Cmd_AUTHIDENT(tClient *Client, char *Args); void Server_Cmd_SETEUSER(tClient *Client, char *Args); void Server_Cmd_ENUMITEMS(tClient *Client, char *Args); void Server_Cmd_ITEMINFO(tClient *Client, char *Args); @@ -86,6 +95,7 @@ const struct sClientCommand { {"USER", Server_Cmd_USER}, {"PASS", Server_Cmd_PASS}, {"AUTOAUTH", Server_Cmd_AUTOAUTH}, + {"AUTHIDENT", Server_Cmd_AUTHIDENT}, {"SETEUSER", Server_Cmd_SETEUSER}, {"ENUM_ITEMS", Server_Cmd_ENUMITEMS}, {"ITEM_INFO", Server_Cmd_ITEMINFO}, @@ -109,6 +119,8 @@ const struct sClientCommand { int gbServer_RunInBackground = 0; char *gsServer_LogFile = "/var/log/dispsrv.log"; char *gsServer_ErrorLog = "/var/log/dispsrv.err"; + int giServer_NumTrustedHosts; +struct in_addr *gaServer_TrustedHosts; // - State variables int giServer_Socket; // Server socket int giServer_NextClientID = 1; // Debug client ID @@ -123,6 +135,19 @@ void Server_Start(void) int client_socket; struct sockaddr_in server_addr, client_addr; + // Parse trusted hosts list + giServer_NumTrustedHosts = Config_GetValueCount("trusted_host"); + gaServer_TrustedHosts = malloc(giServer_NumTrustedHosts * sizeof(*gaServer_TrustedHosts)); + for( int i = 0; i < giServer_NumTrustedHosts; i ++ ) + { + const char *addr = Config_GetValue("trusted_host", i); + + if( inet_aton(addr, &gaServer_TrustedHosts[i]) == 0 ) { + fprintf(stderr, "Invalid IP address '%s'\n", addr); + continue ; + } + } + atexit(Server_Cleanup); // Ignore SIGPIPE (stops crashes when the client exits early) signal(SIGPIPE, SIG_IGN); @@ -147,7 +172,7 @@ void Server_Start(void) return ; } -#if 0 + // Fork into background if( gbServer_RunInBackground ) { int pid = fork(); @@ -158,14 +183,26 @@ void Server_Start(void) } if( pid != 0 ) { // Parent, quit + printf("Forked child %i\n", pid); exit(0); } - // In child, sort out stdin/stdout - reopen(0, "/dev/null", O_READ); - reopen(1, gsServer_LogFile, O_CREAT|O_APPEND); - reopen(2, gsServer_ErrorLog, O_CREAT|O_APPEND); + // In child + // - Sort out stdin/stdout + #if 0 + dup2( open("/dev/null", O_RDONLY, 0644), STDIN_FILENO ); + dup2( open(gsServer_LogFile, O_CREAT|O_APPEND, 0644), STDOUT_FILENO ); + dup2( open(gsServer_ErrorLog, O_CREAT|O_APPEND, 0644), STDERR_FILENO ); + #else + freopen("/dev/null", "r", stdin); + freopen(gsServer_LogFile, "a", stdout); + freopen(gsServer_ErrorLog, "a", stderr); + fprintf(stdout, "OpenDispense 2 Server Started at %lld\n", (long long)time(NULL)); + fprintf(stderr, "OpenDispense 2 Server Started at %lld\n", (long long)time(NULL)); + #endif } -#endif + + // Start the helper thread + StartPeriodicThread(); // Listen if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) { @@ -189,6 +226,7 @@ void Server_Start(void) { uint len = sizeof(client_addr); int bTrusted = 0; + int bRootPort = 0; // Accept a connection client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len); @@ -220,30 +258,46 @@ void Server_Start(void) // Doesn't matter what, localhost is trusted if( ntohl( client_addr.sin_addr.s_addr ) == 0x7F000001 ) bTrusted = 1; - - // Trusted Connections + + // Check if the host is on the trusted list + for( int i = 0; i < giServer_NumTrustedHosts; i ++ ) + { + if( memcmp(&client_addr.sin_addr, &gaServer_TrustedHosts[i], sizeof(struct in_addr)) == 0 ) + { + bTrusted = 1; + break; + } + } + + // Root port (can AUTOAUTH if also a trusted machine if( ntohs(client_addr.sin_port) < 1024 ) + bRootPort = 1; + + #if 0 { // TODO: Make this runtime configurable switch( ntohl( client_addr.sin_addr.s_addr ) ) { case 0x7F000001: // 127.0.0.1 localhost // case 0x825F0D00: // 130.95.13.0 + case 0x825F0D04: // 130.95.13.4 merlo + // case 0x825F0D05: // 130.95.13.5 heathred (MR) case 0x825F0D07: // 130.95.13.7 motsugo case 0x825F0D11: // 130.95.13.17 mermaid case 0x825F0D12: // 130.95.13.18 mussel case 0x825F0D17: // 130.95.13.23 martello case 0x825F0D2A: // 130.95.13.42 meersau - case 0x825F0D42: // 130.95.13.66 heathred + // case 0x825F0D42: // 130.95.13.66 heathred (Clubroom) bTrusted = 1; break; default: break; } } + #endif // TODO: Multithread this? - Server_HandleClient(client_socket, bTrusted); + Server_HandleClient(client_socket, bTrusted, bRootPort); close(client_socket); } @@ -261,7 +315,7 @@ void Server_Cleanup(void) * \param Socket Client socket number/handle * \param bTrusted Is the client trusted? */ -void Server_HandleClient(int Socket, int bTrusted) +void Server_HandleClient(int Socket, int bTrusted, int bRootPort) { char inbuf[INPUT_BUFFER_SIZE]; char *buf = inbuf; @@ -274,7 +328,8 @@ void Server_HandleClient(int Socket, int bTrusted) // Initialise Client info clientInfo.Socket = Socket; clientInfo.ID = giServer_NextClientID ++; - clientInfo.bIsTrusted = bTrusted; + clientInfo.bTrustedHost = bTrusted; + clientInfo.bCanAutoAuth = bTrusted && bRootPort; clientInfo.EffectiveUID = -1; // Read from client @@ -348,9 +403,7 @@ void Server_ParseClientCommand(tClient *Client, char *CommandString) if( Server_int_ParseArgs(1, CommandString, &command, &args, NULL) ) { if( command == NULL ) return ; -// printf("command=%s, args=%s\n", command, args); // Is this an error? (just ignore for now) - //args = ""; } @@ -471,7 +524,7 @@ void Server_Cmd_AUTOAUTH(tClient *Client, char *Args) } // Check if trusted - if( !Client->bIsTrusted ) { + if( !Client->bCanAutoAuth ) { if(giDebugLevel) Debug(Client, "Untrusted client attempting to AUTOAUTH"); sendf(Client->Socket, "401 Untrusted\n"); @@ -479,7 +532,7 @@ void Server_Cmd_AUTOAUTH(tClient *Client, char *Args) } // Get UID - Client->UID = Bank_GetAcctByName( username ); + Client->UID = Bank_GetAcctByName( username, 0 ); if( Client->UID < 0 ) { if(giDebugLevel) Debug(Client, "Unknown user '%s'", username); @@ -504,6 +557,11 @@ void Server_Cmd_AUTOAUTH(tClient *Client, char *Args) return ; } + // Save username + if(Client->Username) + free(Client->Username); + Client->Username = strdup(username); + Client->bIsAuthed = 1; if(giDebugLevel) @@ -512,6 +570,81 @@ void Server_Cmd_AUTOAUTH(tClient *Client, char *Args) sendf(Client->Socket, "200 Auth OK\n"); } +/** + * \brief Authenticate as a user using the IDENT protocol + * + * Usage: AUTHIDENT + */ +void Server_Cmd_AUTHIDENT(tClient *Client, char *Args) +{ + char *username; + int userflags; + const int ident_timeout = 5; + + if( Args != NULL && strlen(Args) ) { + sendf(Client->Socket, "407 AUTHIDENT takes no arguments\n"); + return ; + } + + // Check if trusted + if( !Client->bTrustedHost ) { + if(giDebugLevel) + Debug(Client, "Untrusted client attempting to AUTHIDENT"); + sendf(Client->Socket, "401 Untrusted\n"); + return ; + } + + // Get username via IDENT + username = ident_id(Client->Socket, ident_timeout); + if( !username ) { + perror("AUTHIDENT - IDENT timed out"); + sendf(Client->Socket, "403 Authentication failure: IDENT auth timed out\n"); + return ; + } + + // Get UID + Client->UID = Bank_GetAcctByName( username, 0 ); + if( Client->UID < 0 ) { + if(giDebugLevel) + Debug(Client, "Unknown user '%s'", username); + sendf(Client->Socket, "403 Authentication failure: unknown account\n"); + free(username); + return ; + } + + userflags = Bank_GetFlags(Client->UID); + // You can't be an internal account + if( userflags & USER_FLAG_INTERNAL ) { + if(giDebugLevel) + Debug(Client, "IDENT auth as '%s', not allowed", username); + Client->UID = -1; + sendf(Client->Socket, "403 Authentication failure: that account is internal\n"); + free(username); + return ; + } + + // Disabled accounts + if( userflags & USER_FLAG_DISABLED ) { + Client->UID = -1; + sendf(Client->Socket, "403 Authentication failure: account disabled\n"); + free(username); + return ; + } + + // Save username + if(Client->Username) + free(Client->Username); + Client->Username = strdup(username); + + Client->bIsAuthed = 1; + + if(giDebugLevel) + Debug(Client, "IDENT authenticated as '%s' (%i)", username, Client->UID); + free(username); + + sendf(Client->Socket, "200 Auth OK\n"); +} + /** * \brief Set effective user */ @@ -530,6 +663,12 @@ void Server_Cmd_SETEUSER(tClient *Client, char *Args) sendf(Client->Socket, "407 SETEUSER expects an argument\n"); return ; } + + // Check authentication + if( !Client->bIsAuthed ) { + sendf(Client->Socket, "401 Not Authenticated\n"); + return ; + } // Check user permissions userFlags = Bank_GetFlags(Client->UID); @@ -539,7 +678,7 @@ void Server_Cmd_SETEUSER(tClient *Client, char *Args) } // Set id - Client->EffectiveUID = Bank_GetAcctByName(username); + Client->EffectiveUID = Bank_GetAcctByName(username, 0); if( Client->EffectiveUID == -1 ) { sendf(Client->Socket, "404 User not found\n"); return ; @@ -561,6 +700,13 @@ void Server_Cmd_SETEUSER(tClient *Client, char *Args) return ; } } + + // Disabled accounts + if( userFlags & USER_FLAG_DISABLED ) { + Client->UID = -1; + sendf(Client->Socket, "403 Account disabled\n"); + return ; + } sendf(Client->Socket, "200 User set\n"); } @@ -585,6 +731,12 @@ void Server_int_SendItem(tClient *Client, tItem *Item) } } + if( Item->Price == 0 ) + status = "error"; + // KNOWN HACK: Naming a slot 'dead' disables it + if( strcmp(Item->Name, "dead") == 0 ) + status = "sold"; // Another status? + sendf(Client->Socket, "202 Item %s:%i %s %i %s\n", Item->Handler->Name, Item->ID, status, Item->Price, Item->Name @@ -715,7 +867,7 @@ void Server_Cmd_DISPENSE(tClient *Client, char *Args) case 1: sendf(Client->Socket, "501 Unable to dispense\n"); return ; case 2: sendf(Client->Socket, "402 Poor You\n"); return ; default: - sendf(Client->Socket, "500 Dispense Error\n"); + sendf(Client->Socket, "500 Dispense Error (%i)\n", ret); return ; } } @@ -744,7 +896,7 @@ void Server_Cmd_REFUND(tClient *Client, char *Args) return ; } - uid = Bank_GetAcctByName(username); + uid = Bank_GetAcctByName(username, 0); if( uid == -1 ) { sendf(Client->Socket, "404 Unknown user\n"); return ; @@ -779,6 +931,7 @@ void Server_Cmd_GIVE(tClient *Client, char *Args) sendf(Client->Socket, "407 GIVE takes only 3 arguments\n"); return ; } + // Check for authed if( !Client->bIsAuthed ) { sendf(Client->Socket, "401 Not Authenticated\n"); @@ -786,7 +939,7 @@ void Server_Cmd_GIVE(tClient *Client, char *Args) } // Get recipient - uid = Bank_GetAcctByName(recipient); + uid = Bank_GetAcctByName(recipient, 0); if( uid == -1 ) { sendf(Client->Socket, "404 Invalid target user\n"); return ; @@ -896,8 +1049,26 @@ void Server_Cmd_ADD(tClient *Client, char *Args) return ; } + #if !ROOT_CAN_ADD + if( strcmp( Client->Username, "root" ) == 0 ) { + // Allow adding for new users + if( strcmp(reason, "treasurer: new user") != 0 ) { + sendf(Client->Socket, "403 Root may not add\n"); + return ; + } + } + #endif + + #if HACK_NO_REFUNDS + if( strstr(reason, "refund") != NULL || strstr(reason, "misdispense") != NULL ) + { + sendf(Client->Socket, "499 Don't use `dispense acct` for refunds, use `dispense refund` (and `dispense -G` to get item IDs)\n"); + return ; + } + #endif + // Get recipient - uid = Bank_GetAcctByName(user); + uid = Bank_GetAcctByName(user, 0); if( uid == -1 ) { sendf(Client->Socket, "404 Invalid user\n"); return ; @@ -958,7 +1129,7 @@ void Server_Cmd_SET(tClient *Client, char *Args) } // Get recipient - uid = Bank_GetAcctByName(user); + uid = Bank_GetAcctByName(user, 0); if( uid == -1 ) { sendf(Client->Socket, "404 Invalid user\n"); return ; @@ -1171,7 +1342,7 @@ void Server_Cmd_USERINFO(tClient *Client, char *Args) if( giDebugLevel ) Debug(Client, "User Info '%s'", user); // Get recipient - uid = Bank_GetAcctByName(user); + uid = Bank_GetAcctByName(user, 0); if( giDebugLevel >= 2 ) Debug(Client, "uid = %i", uid); if( uid == -1 ) { @@ -1226,6 +1397,12 @@ void Server_Cmd_USERADD(tClient *Client, char *Args) return ; } + // Check authentication + if( !Client->bIsAuthed ) { + sendf(Client->Socket, "401 Not Authenticated\n"); + return ; + } + // Check permissions if( !(Bank_GetFlags(Client->UID) & USER_FLAG_ADMIN) ) { sendf(Client->Socket, "403 Not a coke admin\n"); @@ -1249,13 +1426,22 @@ void Server_Cmd_USERADD(tClient *Client, char *Args) void Server_Cmd_USERFLAGS(tClient *Client, char *Args) { - char *username, *flags; + char *username, *flags, *reason=NULL; int mask=0, value=0; int uid; // Parse arguments - if( Server_int_ParseArgs(0, Args, &username, &flags, NULL) ) { - sendf(Client->Socket, "407 USER_FLAGS takes 2 arguments\n"); + if( Server_int_ParseArgs(1, Args, &username, &flags, &reason, NULL) ) { + if( !flags ) { + sendf(Client->Socket, "407 USER_FLAGS takes at least 2 arguments\n"); + return ; + } + reason = ""; + } + + // Check authentication + if( !Client->bIsAuthed ) { + sendf(Client->Socket, "401 Not Authenticated\n"); return ; } @@ -1266,7 +1452,7 @@ void Server_Cmd_USERFLAGS(tClient *Client, char *Args) } // Get UID - uid = Bank_GetAcctByName(username); + uid = Bank_GetAcctByName(username, 0); if( uid == -1 ) { sendf(Client->Socket, "404 User '%s' not found\n", username); return ; @@ -1282,6 +1468,10 @@ void Server_Cmd_USERFLAGS(tClient *Client, char *Args) // Apply flags Bank_SetFlags(uid, mask, value); + + // Log the change + Log_Info("Updated '%s' with flag set '%s' by '%s' - Reason: %s", + username, flags, Client->Username, reason); // Return OK sendf(Client->Socket, "200 User Updated\n"); @@ -1318,19 +1508,18 @@ void Server_Cmd_UPDATEITEM(tClient *Client, char *Args) price = atoi(price_str); if( price <= 0 && price_str[0] != '0' ) { - sendf(CLient->Socket, "407 Invalid price set\n"); + sendf(Client->Socket, "407 Invalid price set\n"); } - // Update the item - free(item->Name); - item->Name = strdup(description); - item->Price = price; - - // Update item file - Items_UpdateFile(); - - // Return OK - sendf(Client->Socket, "200 Item updated\n"); + switch( DispenseUpdateItem( Client->UID, item, description, price ) ) + { + case 0: + // Return OK + sendf(Client->Socket, "200 Item updated\n"); + break; + default: + break; + } } // --- INTERNAL HELPERS ---