Added new user command (wheel only)
[tpg/opendispense2.git] / src / server / server.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  *
5  * server.c - Client Server Code
6  *
7  * This file is licenced under the 3-clause BSD Licence. See the file
8  * COPYING for full details.
9  */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include "common.h"
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <limits.h>
19 #include <stdarg.h>
20
21 // HACKS
22 #define HACK_TPG_NOAUTH 1
23 #define HACK_ROOT_NOAUTH        1
24
25 #define DEBUG_TRACE_CLIENT      0
26
27 // Statistics
28 #define MAX_CONNECTION_QUEUE    5
29 #define INPUT_BUFFER_SIZE       256
30
31 #define HASH_TYPE       SHA1
32 #define HASH_LENGTH     20
33
34 #define MSG_STR_TOO_LONG        "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
35
36 // === TYPES ===
37 typedef struct sClient
38 {
39          int    Socket; // Client socket ID
40          int    ID;     // Client ID
41          
42          int    bIsTrusted;     // Is the connection from a trusted host/port
43         
44         char    *Username;
45         char    Salt[9];
46         
47          int    UID;
48          int    bIsAuthed;
49 }       tClient;
50
51 // === PROTOTYPES ===
52 void    Server_Start(void);
53 void    Server_Cleanup(void);
54 void    Server_HandleClient(int Socket, int bTrusted);
55 void    Server_ParseClientCommand(tClient *Client, char *CommandString);
56 // --- Commands ---
57 void    Server_Cmd_USER(tClient *Client, char *Args);
58 void    Server_Cmd_PASS(tClient *Client, char *Args);
59 void    Server_Cmd_AUTOAUTH(tClient *Client, char *Args);
60 void    Server_Cmd_ENUMITEMS(tClient *Client, char *Args);
61 void    Server_Cmd_ITEMINFO(tClient *Client, char *Args);
62 void    Server_Cmd_DISPENSE(tClient *Client, char *Args);
63 void    Server_Cmd_GIVE(tClient *Client, char *Args);
64 void    Server_Cmd_ADD(tClient *Client, char *Args);
65 void    Server_Cmd_ENUMUSERS(tClient *Client, char *Args);
66 void    Server_Cmd_USERINFO(tClient *Client, char *Args);
67 void    _SendUserInfo(tClient *Client, int UserID);
68 void    Server_Cmd_USERADD(tClient *Client, char *Args);
69 void    Server_Cmd_USERFLAGS(tClient *Client, char *Args);
70 // --- Helpers ---
71  int    sendf(int Socket, const char *Format, ...);
72  int    GetUserAuth(const char *Salt, const char *Username, const uint8_t *Hash);
73 void    HexBin(uint8_t *Dest, char *Src, int BufSize);
74
75 // === GLOBALS ===
76  int    giServer_Port = 1020;
77  int    giServer_NextClientID = 1;
78 // - Commands
79 struct sClientCommand {
80         char    *Name;
81         void    (*Function)(tClient *Client, char *Arguments);
82 }       gaServer_Commands[] = {
83         {"USER", Server_Cmd_USER},
84         {"PASS", Server_Cmd_PASS},
85         {"AUTOAUTH", Server_Cmd_AUTOAUTH},
86         {"ENUM_ITEMS", Server_Cmd_ENUMITEMS},
87         {"ITEM_INFO", Server_Cmd_ITEMINFO},
88         {"DISPENSE", Server_Cmd_DISPENSE},
89         {"GIVE", Server_Cmd_GIVE},
90         {"ADD", Server_Cmd_ADD},
91         {"ENUM_USERS", Server_Cmd_ENUMUSERS},
92         {"USER_INFO", Server_Cmd_USERINFO},
93         {"USER_ADD", Server_Cmd_USERADD},
94         {"USER_FLAGS", Server_Cmd_USERFLAGS}
95 };
96 #define NUM_COMMANDS    (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
97  int    giServer_Socket;
98
99 // === CODE ===
100 /**
101  * \brief Open listenting socket and serve connections
102  */
103 void Server_Start(void)
104 {
105          int    client_socket;
106         struct sockaddr_in      server_addr, client_addr;
107
108         atexit(Server_Cleanup);
109
110         // Create Server
111         giServer_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
112         if( giServer_Socket < 0 ) {
113                 fprintf(stderr, "ERROR: Unable to create server socket\n");
114                 return ;
115         }
116         
117         // Make listen address
118         memset(&server_addr, 0, sizeof(server_addr));
119         server_addr.sin_family = AF_INET;       // Internet Socket
120         server_addr.sin_addr.s_addr = htonl(INADDR_ANY);        // Listen on all interfaces
121         server_addr.sin_port = htons(giServer_Port);    // Port
122
123         // Bind
124         if( bind(giServer_Socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
125                 fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
126                 perror("Binding");
127                 return ;
128         }
129         
130         // Listen
131         if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
132                 fprintf(stderr, "ERROR: Unable to listen to socket\n");
133                 perror("Listen");
134                 return ;
135         }
136         
137         printf("Listening on 0.0.0.0:%i\n", giServer_Port);
138         
139         for(;;)
140         {
141                 uint    len = sizeof(client_addr);
142                  int    bTrusted = 0;
143                 
144                 client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len);
145                 if(client_socket < 0) {
146                         fprintf(stderr, "ERROR: Unable to accept client connection\n");
147                         return ;
148                 }
149                 
150                 if(giDebugLevel >= 2) {
151                         char    ipstr[INET_ADDRSTRLEN];
152                         inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, INET_ADDRSTRLEN);
153                         printf("Client connection from %s:%i\n",
154                                 ipstr, ntohs(client_addr.sin_port));
155                 }
156                 
157                 // Trusted Connections
158                 if( ntohs(client_addr.sin_port) < 1024 )
159                 {
160                         // TODO: Make this runtime configurable
161                         switch( ntohl( client_addr.sin_addr.s_addr ) )
162                         {
163                         case 0x7F000001:        // 127.0.0.1    localhost
164                         //case 0x825E0D00:      // 130.95.13.0
165                         case 0x825E0D12:        // 130.95.13.18 mussel
166                         case 0x825E0D17:        // 130.95.13.23 martello
167                                 bTrusted = 1;
168                                 break;
169                         default:
170                                 break;
171                         }
172                 }
173                 
174                 // TODO: Multithread this?
175                 Server_HandleClient(client_socket, bTrusted);
176                 
177                 close(client_socket);
178         }
179 }
180
181 void Server_Cleanup(void)
182 {
183         printf("Close(%i)\n", giServer_Socket);
184         close(giServer_Socket);
185 }
186
187 /**
188  * \brief Reads from a client socket and parses the command strings
189  * \param Socket        Client socket number/handle
190  * \param bTrusted      Is the client trusted?
191  */
192 void Server_HandleClient(int Socket, int bTrusted)
193 {
194         char    inbuf[INPUT_BUFFER_SIZE];
195         char    *buf = inbuf;
196          int    remspace = INPUT_BUFFER_SIZE-1;
197          int    bytes = -1;
198         tClient clientInfo = {0};
199         
200         // Initialise Client info
201         clientInfo.Socket = Socket;
202         clientInfo.ID = giServer_NextClientID ++;
203         clientInfo.bIsTrusted = bTrusted;
204         
205         // Read from client
206         /*
207          * Notes:
208          * - The `buf` and `remspace` variables allow a line to span several
209          *   calls to recv(), if a line is not completed in one recv() call
210          *   it is saved to the beginning of `inbuf` and `buf` is updated to
211          *   the end of it.
212          */
213         while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
214         {
215                 char    *eol, *start;
216                 buf[bytes] = '\0';      // Allow us to use stdlib string functions on it
217                 
218                 // Split by lines
219                 start = inbuf;
220                 while( (eol = strchr(start, '\n')) )
221                 {
222                         *eol = '\0';
223                         
224                         Server_ParseClientCommand(&clientInfo, start);
225                         
226                         start = eol + 1;
227                 }
228                 
229                 // Check if there was an incomplete line
230                 if( *start != '\0' ) {
231                          int    tailBytes = bytes - (start-buf);
232                         // Roll back in buffer
233                         memcpy(inbuf, start, tailBytes);
234                         remspace -= tailBytes;
235                         if(remspace == 0) {
236                                 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
237                                 buf = inbuf;
238                                 remspace = INPUT_BUFFER_SIZE - 1;
239                         }
240                 }
241                 else {
242                         buf = inbuf;
243                         remspace = INPUT_BUFFER_SIZE - 1;
244                 }
245         }
246         
247         // Check for errors
248         if( bytes < 0 ) {
249                 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
250                 return ;
251         }
252         
253         if(giDebugLevel >= 2) {
254                 printf("Client %i: Disconnected\n", clientInfo.ID);
255         }
256 }
257
258 /**
259  * \brief Parses a client command and calls the required helper function
260  * \param Client        Pointer to client state structure
261  * \param CommandString Command from client (single line of the command)
262  * \return Heap String to return to the client
263  */
264 void Server_ParseClientCommand(tClient *Client, char *CommandString)
265 {
266         char    *space, *args;
267          int    i;
268         #if 0
269         char    **argList;
270          int    numArgs = 0;
271         #endif
272         
273         // Split at first space
274         space = strchr(CommandString, ' ');
275         if(space == NULL) {
276                 args = NULL;
277         }
278         else {
279                 *space = '\0';
280                 args = space + 1;
281                 while( *space == ' ' )  space ++;
282                 
283                 #if 0
284                 // Count arguments
285                 numArgs = 1;
286                 for( i = 0; args[i]; )
287                 {
288                         while( CommandString[i] != ' ' ) {
289                                 if( CommandString[i] == '"' ) {
290                                         while( !(CommandString[i] != '\\' CommandString[i+1] == '"' ) )
291                                                 i ++;
292                                         i ++;
293                                 }
294                                 i ++;
295                         }
296                         numArgs ++;
297                         while( CommandString[i] == ' ' )        i ++;
298                 }
299                 #endif
300         }
301         
302         
303         // Find command
304         for( i = 0; i < NUM_COMMANDS; i++ )
305         {
306                 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) {
307                         gaServer_Commands[i].Function(Client, args);
308                         return ;
309                 }
310         }
311         
312         sendf(Client->Socket, "400 Unknown Command\n");
313 }
314
315 // ---
316 // Commands
317 // ---
318 /**
319  * \brief Set client username
320  * 
321  * Usage: USER <username>
322  */
323 void Server_Cmd_USER(tClient *Client, char *Args)
324 {       
325         // Debug!
326         if( giDebugLevel )
327                 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
328         
329         // Save username
330         if(Client->Username)
331                 free(Client->Username);
332         Client->Username = strdup(Args);
333         
334         #if USE_SALT
335         // Create a salt (that changes if the username is changed)
336         // Yes, I know, I'm a little paranoid, but who isn't?
337         Client->Salt[0] = 0x21 + (rand()&0x3F);
338         Client->Salt[1] = 0x21 + (rand()&0x3F);
339         Client->Salt[2] = 0x21 + (rand()&0x3F);
340         Client->Salt[3] = 0x21 + (rand()&0x3F);
341         Client->Salt[4] = 0x21 + (rand()&0x3F);
342         Client->Salt[5] = 0x21 + (rand()&0x3F);
343         Client->Salt[6] = 0x21 + (rand()&0x3F);
344         Client->Salt[7] = 0x21 + (rand()&0x3F);
345         
346         // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA])
347         sendf(Client->Socket, "100 SALT %s\n", Client->Salt);
348         #else
349         sendf(Client->Socket, "100 User Set\n");
350         #endif
351 }
352
353 /**
354  * \brief Authenticate as a user
355  * 
356  * Usage: PASS <hash>
357  */
358 void Server_Cmd_PASS(tClient *Client, char *Args)
359 {
360         uint8_t clienthash[HASH_LENGTH] = {0};
361         
362         // Read user's hash
363         HexBin(clienthash, Args, HASH_LENGTH);
364         
365         // TODO: Decrypt password passed
366         
367         Client->UID = GetUserAuth(Client->Salt, Client->Username, clienthash);
368
369         if( Client->UID != -1 ) {
370                 Client->bIsAuthed = 1;
371                 sendf(Client->Socket, "200 Auth OK\n");
372                 return ;
373         }
374
375         if( giDebugLevel ) {
376                  int    i;
377                 printf("Client %i: Password hash ", Client->ID);
378                 for(i=0;i<HASH_LENGTH;i++)
379                         printf("%02x", clienthash[i]&0xFF);
380                 printf("\n");
381         }
382         
383         sendf(Client->Socket, "401 Auth Failure\n");
384 }
385
386 /**
387  * \brief Authenticate as a user without a password
388  * 
389  * Usage: AUTOAUTH <user>
390  */
391 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
392 {
393         char    *spos = strchr(Args, ' ');
394         if(spos)        *spos = '\0';   // Remove characters after the ' '
395         
396         // Check if trusted
397         if( !Client->bIsTrusted ) {
398                 if(giDebugLevel)
399                         printf("Client %i: Untrusted client attempting to AUTOAUTH\n", Client->ID);
400                 sendf(Client->Socket, "401 Untrusted\n");
401                 return ;
402         }
403         
404         // Get UID
405         Client->UID = GetUserID( Args );        
406         if( Client->UID < 0 ) {
407                 if(giDebugLevel)
408                         printf("Client %i: Unknown user '%s'\n", Client->ID, Args);
409                 sendf(Client->Socket, "401 Auth Failure\n");
410                 return ;
411         }
412         
413         if(giDebugLevel)
414                 printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID);
415         
416         sendf(Client->Socket, "200 Auth OK\n");
417 }
418
419 /**
420  * \brief Enumerate the items that the server knows about
421  */
422 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
423 {
424          int    i;
425
426         sendf(Client->Socket, "201 Items %i\n", giNumItems);
427
428         for( i = 0; i < giNumItems; i ++ ) {
429                 sendf(Client->Socket,
430                         "202 Item %s:%i %i %s\n",
431                          gaItems[i].Handler->Name, gaItems[i].ID, gaItems[i].Price, gaItems[i].Name
432                          );
433         }
434
435         sendf(Client->Socket, "200 List end\n");
436 }
437
438 tItem *_GetItemFromString(char *String)
439 {
440         tHandler        *handler;
441         char    *type = String;
442         char    *colon = strchr(String, ':');
443          int    num, i;
444         
445         if( !colon ) {
446                 return NULL;
447         }
448
449         num = atoi(colon+1);
450         *colon = '\0';
451
452         // Find handler
453         handler = NULL;
454         for( i = 0; i < giNumHandlers; i ++ )
455         {
456                 if( strcmp(gaHandlers[i]->Name, type) == 0) {
457                         handler = gaHandlers[i];
458                         break;
459                 }
460         }
461         if( !handler ) {
462                 return NULL;
463         }
464
465         // Find item
466         for( i = 0; i < giNumItems; i ++ )
467         {
468                 if( gaItems[i].Handler != handler )     continue;
469                 if( gaItems[i].ID != num )      continue;
470                 return &gaItems[i];
471         }
472         return NULL;
473 }
474
475 /**
476  * \brief Fetch information on a specific item
477  */
478 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
479 {
480         tItem   *item = _GetItemFromString(Args);
481         
482         if( !item ) {
483                 sendf(Client->Socket, "406 Bad Item ID\n");
484                 return ;
485         }
486         
487         sendf(Client->Socket,
488                 "202 Item %s:%i %i %s\n",
489                  item->Handler->Name, item->ID, item->Price, item->Name
490                  );
491 }
492
493 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
494 {
495         tItem   *item;
496          int    ret;
497         if( !Client->bIsAuthed ) {
498                 sendf(Client->Socket, "401 Not Authenticated\n");
499                 return ;
500         }
501
502         item = _GetItemFromString(Args);
503         if( !item ) {
504                 sendf(Client->Socket, "406 Bad Item ID\n");
505                 return ;
506         }
507
508         switch( ret = DispenseItem( Client->UID, item ) )
509         {
510         case 0: sendf(Client->Socket, "200 Dispense OK\n");     return ;
511         case 1: sendf(Client->Socket, "501 Unable to dispense\n");      return ;
512         case 2: sendf(Client->Socket, "402 Poor You\n");        return ;
513         default:
514                 sendf(Client->Socket, "500 Dispense Error\n");
515                 return ;
516         }
517 }
518
519 void Server_Cmd_GIVE(tClient *Client, char *Args)
520 {
521         char    *recipient, *ammount, *reason;
522          int    uid, iAmmount;
523         
524         if( !Client->bIsAuthed ) {
525                 sendf(Client->Socket, "401 Not Authenticated\n");
526                 return ;
527         }
528
529         recipient = Args;
530
531         ammount = strchr(Args, ' ');
532         if( !ammount ) {
533                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
534                 return ;
535         }
536         *ammount = '\0';
537         ammount ++;
538
539         reason = strchr(ammount, ' ');
540         if( !reason ) {
541                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
542                 return ;
543         }
544         *reason = '\0';
545         reason ++;
546
547         // Get recipient
548         uid = GetUserID(recipient);
549         if( uid == -1 ) {
550                 sendf(Client->Socket, "404 Invalid target user\n");
551                 return ;
552         }
553
554         // Parse ammount
555         iAmmount = atoi(ammount);
556         if( iAmmount <= 0 ) {
557                 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
558                 return ;
559         }
560
561         // Do give
562         switch( DispenseGive(Client->UID, uid, iAmmount, reason) )
563         {
564         case 0:
565                 sendf(Client->Socket, "200 Give OK\n");
566                 return ;
567         case 2:
568                 sendf(Client->Socket, "402 Poor You\n");
569                 return ;
570         default:
571                 sendf(Client->Socket, "500 Unknown error\n");
572                 return ;
573         }
574 }
575
576 void Server_Cmd_ADD(tClient *Client, char *Args)
577 {
578         char    *user, *ammount, *reason;
579          int    uid, iAmmount;
580         
581         if( !Client->bIsAuthed ) {
582                 sendf(Client->Socket, "401 Not Authenticated\n");
583                 return ;
584         }
585
586         user = Args;
587
588         ammount = strchr(Args, ' ');
589         if( !ammount ) {
590                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
591                 return ;
592         }
593         *ammount = '\0';
594         ammount ++;
595
596         reason = strchr(ammount, ' ');
597         if( !reason ) {
598                 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
599                 return ;
600         }
601         *reason = '\0';
602         reason ++;
603
604         // TODO: Check if the current user is in coke/higher
605         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_COKE ) {
606                 sendf(Client->Socket, "403 Not in coke\n");
607                 return ;
608         }
609
610         // Get recipient
611         uid = GetUserID(user);
612         if( uid == -1 ) {
613                 sendf(Client->Socket, "404 Invalid user\n");
614                 return ;
615         }
616
617         // Parse ammount
618         iAmmount = atoi(ammount);
619         if( iAmmount == 0 && ammount[0] != '0' ) {
620                 sendf(Client->Socket, "407 Invalid Argument\n");
621                 return ;
622         }
623
624         // Do give
625         switch( DispenseAdd(uid, Client->UID, iAmmount, reason) )
626         {
627         case 0:
628                 sendf(Client->Socket, "200 Add OK\n");
629                 return ;
630         case 2:
631                 sendf(Client->Socket, "402 Poor Guy\n");
632                 return ;
633         default:
634                 sendf(Client->Socket, "500 Unknown error\n");
635                 return ;
636         }
637 }
638
639 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
640 {
641          int    i, numRet = 0;
642          int    maxBal = INT_MAX, minBal = INT_MIN;
643          int    numUsr = GetMaxID();
644         
645         // Parse arguments
646         if( Args && strlen(Args) )
647         {
648                 char    *min = Args, *max;
649                 
650                 max = strchr(Args, ' ');
651                 if( max ) {
652                         *max = '\0';
653                         max ++;
654                 }
655                 
656                 // If <minBal> != "-"
657                 if( strcmp(min, "-") != 0 )
658                         minBal = atoi(min);
659                 // If <maxBal> != "-"
660                 if( max && strcmp(max, "-") != 0 )
661                         maxBal = atoi(max);
662         }
663         
664         // Get return number
665         for( i = 0; i < numUsr; i ++ )
666         {
667                 int bal = GetBalance(i);
668                 
669                 if( bal == INT_MIN )    continue;
670                 
671                 if( bal < minBal )      continue;
672                 if( bal > maxBal )      continue;
673                 
674                 numRet ++;
675         }
676         
677         // Send count
678         sendf(Client->Socket, "201 Users %i\n", numRet);
679         
680         for( i = 0; i < numUsr; i ++ )
681         {
682                 int bal = GetBalance(i);
683                 
684                 if( bal == INT_MIN )    continue;
685                 
686                 if( bal < minBal )      continue;
687                 if( bal > maxBal )      continue;
688                 
689                 _SendUserInfo(Client, i);
690         }
691         
692         sendf(Client->Socket, "200 List End\n");
693 }
694
695 void Server_Cmd_USERINFO(tClient *Client, char *Args)
696 {
697          int    uid;
698         char    *user = Args;
699         char    *space;
700         
701         space = strchr(user, ' ');
702         if(space)       *space = '\0';
703         
704         // Get recipient
705         uid = GetUserID(user);
706         if( uid == -1 ) {
707                 sendf(Client->Socket, "404 Invalid user");
708                 return ;
709         }
710         
711         _SendUserInfo(Client, uid);
712 }
713
714 void _SendUserInfo(tClient *Client, int UserID)
715 {
716         char    *type, *disabled="";
717          int    flags = GetFlags(UserID);
718         
719         switch( flags & USER_FLAG_TYPEMASK )
720         {
721         default:
722         case USER_TYPE_NORMAL:  type = "user";  break;
723         case USER_TYPE_COKE:    type = "coke";  break;
724         case USER_TYPE_WHEEL:   type = "wheel"; break;
725         case USER_TYPE_GOD:     type = "meta";  break;
726         }
727         
728         if( flags & USER_FLAG_DISABLED )
729                 disabled = ",disabled";
730         
731         // TODO: User flags/type
732         sendf(
733                 Client->Socket, "202 User %s %i %s%s\n",
734                 GetUserName(UserID), GetBalance(UserID),
735                 type, disabled
736                 );
737 }
738
739 void Server_Cmd_USERADD(tClient *Client, char *Args)
740 {
741         char    *username, *space;
742         
743         // Check permissions
744         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_WHEEL ) {
745                 sendf(Client->Socket, "403 Not Wheel\n");
746                 return ;
747         }
748         
749         // Read arguments
750         username = Args;
751         while( *username == ' ' )       username ++;
752         space = strchr(username, ' ');
753         if(space)       *space = '\0';
754         
755         // Try to create user
756         if( CreateUser(username) == -1 ) {
757                 sendf(Client->Socket, "404 User exists\n");
758                 return ;
759         }
760         
761         sendf(Client->Socket, "200 User Added\n");
762 }
763
764 void Server_Cmd_USERFLAGS(tClient *Client, char *Args)
765 {
766         char    *username, *flags;
767         char    *space;
768         
769         // Check permissions
770         if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_WHEEL ) {
771                 sendf(Client->Socket, "403 Not Wheel\n");
772                 return ;
773         }
774         
775         // Read arguments
776         // - Username
777         username = Args;
778         while( *username == ' ' )       username ++;
779         space = strchr(username, ' ');
780         if(!space) {
781                 sendf(Client->Socket, "407 USER_FLAGS requires 2 arguments, 1 given\n");
782                 return ;
783         }
784         // - Flags
785         flags = space + 1;
786         while( *flags == ' ' )  flags ++;
787         space = strchr(flags, ' ');
788         if(space)       *space = '\0';
789         
790         printf("Username = '%s', flags = '%s'\n", username, flags);
791         
792         sendf(Client->Socket, "200 User Updated\n");
793 }
794
795 /**
796  * \brief Authenticate a user
797  * \return User ID, or -1 if authentication failed
798  */
799 int GetUserAuth(const char *Salt, const char *Username, const uint8_t *ProvidedHash)
800 {
801         #if 0
802         uint8_t h[20];
803          int    ofs = strlen(Username) + strlen(Salt);
804         char    input[ ofs + 40 + 1];
805         char    tmp[4 + strlen(Username) + 1];  // uid=%s
806         #endif
807         
808         #if HACK_TPG_NOAUTH
809         if( strcmp(Username, "tpg") == 0 )
810                 return GetUserID("tpg");
811         #endif
812         #if HACK_ROOT_NOAUTH
813         if( strcmp(Username, "root") == 0 ) {
814                 int ret = GetUserID("root");
815                 if( ret == -1 )
816                         return CreateUser("root");
817                 return ret;
818         }
819         #endif
820         
821         #if 0
822         //
823         strcpy(input, Username);
824         strcpy(input, Salt);
825         // TODO: Get user's SHA-1 hash
826         sprintf(tmp, "uid=%s", Username);
827         ldap_search_s(ld, "", LDAP_SCOPE_BASE, tmp, "userPassword", 0, res);
828         
829         sprintf(input+ofs, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
830                 h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9],
831                 h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19]
832                 );
833         // Then create the hash from the provided salt
834         // Compare that with the provided hash
835         #endif
836         
837         return -1;
838 }
839
840 // --- INTERNAL HELPERS ---
841 int sendf(int Socket, const char *Format, ...)
842 {
843         va_list args;
844          int    len;
845         
846         va_start(args, Format);
847         len = vsnprintf(NULL, 0, Format, args);
848         va_end(args);
849         
850         {
851                 char    buf[len+1];
852                 va_start(args, Format);
853                 vsnprintf(buf, len+1, Format, args);
854                 va_end(args);
855                 
856                 #if DEBUG_TRACE_CLIENT
857                 printf("sendf: %s", buf);
858                 #endif
859                 
860                 return send(Socket, buf, len, 0);
861         }
862 }
863
864 // TODO: Move to another file
865 void HexBin(uint8_t *Dest, char *Src, int BufSize)
866 {
867          int    i;
868         for( i = 0; i < BufSize; i ++ )
869         {
870                 uint8_t val = 0;
871                 
872                 if('0' <= *Src && *Src <= '9')
873                         val |= (*Src-'0') << 4;
874                 else if('A' <= *Src && *Src <= 'F')
875                         val |= (*Src-'A'+10) << 4;
876                 else if('a' <= *Src && *Src <= 'f')
877                         val |= (*Src-'a'+10) << 4;
878                 else
879                         break;
880                 Src ++;
881                 
882                 if('0' <= *Src && *Src <= '9')
883                         val |= (*Src-'0');
884                 else if('A' <= *Src && *Src <= 'F')
885                         val |= (*Src-'A'+10);
886                 else if('a' <= *Src && *Src <= 'f')
887                         val |= (*Src-'a'+10);
888                 else
889                         break;
890                 Src ++;
891                 
892                 Dest[i] = val;
893         }
894         for( ; i < BufSize; i++ )
895                 Dest[i] = 0;
896 }
897
898 /**
899  * \brief Decode a Base64 value
900  */
901 int UnBase64(uint8_t *Dest, char *Src, int BufSize)
902 {
903         uint32_t        val;
904          int    i, j;
905         char    *start_src = Src;
906         
907         for( i = 0; i+2 < BufSize; i += 3 )
908         {
909                 val = 0;
910                 for( j = 0; j < 4; j++, Src ++ ) {
911                         if('A' <= *Src && *Src <= 'Z')
912                                 val |= (*Src - 'A') << ((3-j)*6);
913                         else if('a' <= *Src && *Src <= 'z')
914                                 val |= (*Src - 'a' + 26) << ((3-j)*6);
915                         else if('0' <= *Src && *Src <= '9')
916                                 val |= (*Src - '0' + 52) << ((3-j)*6);
917                         else if(*Src == '+')
918                                 val |= 62 << ((3-j)*6);
919                         else if(*Src == '/')
920                                 val |= 63 << ((3-j)*6);
921                         else if(!*Src)
922                                 break;
923                         else if(*Src != '=')
924                                 j --;   // Ignore invalid characters
925                 }
926                 Dest[i  ] = (val >> 16) & 0xFF;
927                 Dest[i+1] = (val >> 8) & 0xFF;
928                 Dest[i+2] = val & 0xFF;
929                 if(j != 4)      break;
930         }
931         
932         // Finish things off
933         if(i   < BufSize)
934                 Dest[i] = (val >> 16) & 0xFF;
935         if(i+1 < BufSize)
936                 Dest[i+1] = (val >> 8) & 0xFF;
937         
938         return Src - start_src;
939 }

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