Server - Fixed SETEUSER not checking disabled flag
[tpg/opendispense2.git] / src / server / server.c
index 1f813d4..66d1062 100644 (file)
 #include <string.h>
 #include <limits.h>
 #include <stdarg.h>
-#include <signal.h>
+#include <signal.h>    // Signal handling
+#include <ident.h>     // AUTHIDENT
+#include <time.h>      // time(2)
 
 #define        DEBUG_TRACE_CLIENT      0
+#define HACK_NO_REFUNDS        1
 
 // Statistics
 #define MAX_CONNECTION_QUEUE   5
@@ -32,6 +35,9 @@
 
 #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
 {
@@ -57,6 +63,7 @@ void  Server_ParseClientCommand(tClient *Client, char *CommandString);
 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);
@@ -87,6 +94,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},
@@ -151,7 +159,6 @@ void Server_Start(void)
        // Fork into background
        if( gbServer_RunInBackground )
        {
-               int newin, newout, newerr;
                int pid = fork();
                if( pid == -1 ) {
                        fprintf(stderr, "ERROR: Unable to fork\n");
@@ -160,17 +167,26 @@ void Server_Start(void)
                }
                if( pid != 0 ) {
                        // Parent, quit
+                       printf("Forked child %i\n", pid);
                        exit(0);
                }
                // In child
                // - Sort out stdin/stdout
-               newin  = open("/dev/null", O_RDONLY);
-               newout = open(gsServer_LogFile, O_CREAT|O_APPEND, 0644);
-               newerr = open(gsServer_ErrorLog, O_CREAT|O_APPEND, 0644);
-               dup2(newin, 0);
-               dup2(newout, 1);
-               dup2(newerr, 2);
+               #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
        }
+
+       // Start the helper thread
+       StartPeriodicThread();
        
        // Listen
        if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
@@ -234,12 +250,14 @@ void Server_Start(void)
                        {
                        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:
@@ -507,6 +525,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)
@@ -515,6 +538,95 @@ 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;
+       socklen_t len;
+       struct sockaddr_in client_addr;
+       uint32_t  client_ip;
+
+       if( Args != NULL && strlen(Args) ) {
+               sendf(Client->Socket, "407 AUTHIDENT takes no arguments\n");
+               return ;
+       }
+
+       // Check if trusted (only works with INET sockets at present)
+       len = sizeof(client_addr);
+       if( getpeername(Client->Socket, (struct sockaddr*)&client_addr, &len) == -1 ) {
+               Debug(Client, "500 getpeername() failed\n");
+               perror("Getting AUTHIDENT peer name");
+               sendf(Client->Socket, "500 getpeername() failed\n");
+               return ;
+       }
+
+       client_ip = client_addr.sin_addr.s_addr;
+       if(giDebugLevel >= 2) {
+               Debug(Client, "client_ip = %x, ntohl(client_ip) = %x", client_ip, ntohl(client_ip));
+       }
+       if( ntohl(client_ip) != 0x7F000001 && (ntohl(client_ip) & IDENT_TRUSTED_NETMASK) != IDENT_TRUSTED_NETWORK ) {
+                       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 ) {
+               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
  */
@@ -570,6 +682,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");
 }
@@ -728,7 +847,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 ;
        }
 }
@@ -910,6 +1029,24 @@ 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, 0);
        if( uid == -1 ) {
@@ -1313,8 +1450,8 @@ void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
        Bank_SetFlags(uid, mask, value);
 
        // Log the change
-       Log_Info("Updated '%s' with flag set '%s' - Reason: %s",
-               username, flags, reason);
+       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");

UCC git Repository :: git.ucc.asn.au