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

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