]> andersk Git - moira.git/blob - reg_svr/reg_svr.pc
A user may have both a deleted account and a status 8 account, so we
[moira.git] / reg_svr / reg_svr.pc
1 /* $Id$
2  *
3  * Server for user registration with Moira and Kerberos.
4  *
5  * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6  * For copying and distribution information, please see the file
7  * <mit-copyright.h>.
8  */
9
10 #include <mit-copyright.h>
11 #include <moira.h>
12 #include <mr_private.h>
13 #include <moira_schema.h>
14 #include <moira_site.h>
15 #include "reg_svr.h"
16
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <sys/stat.h>
20 #include <sys/time.h>
21 #include <sys/utsname.h>
22
23 #include <netinet/in.h>
24 #include <netdb.h>
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include <com_err.h>
36 #include <krb.h>
37
38 EXEC SQL INCLUDE sqlca;
39
40 RCSID("$Header$");
41
42 char *whoami, *hostname, *shorthostname;
43
44 char *find_usernames(char *first, char *middle, char *last);
45 void fixname(char *name);
46 int register_user(int uid, char *username);
47 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar);
48 void sigshut(int);
49
50 reg_client *cl = NULL;
51 enum { RS_RUNNING, RS_SLEEPING, RS_EXITING } state = RS_RUNNING;
52
53 int main(int argc, char **argv)
54 {
55   int listener, nfds, i, clientid = 0;
56   fd_set readfds, xreadfds;
57   reg_client *clients;
58   int nclients, clientssize;
59   long status;
60   char *db = "moira";
61   struct utsname uts;
62   struct hostent *h;
63   struct sigaction sa;
64   struct stat st;
65
66   whoami = strrchr(argv[0], '/');
67   whoami = whoami ? whoami + 1 : argv[0];
68
69   set_com_err_hook(mr_com_err);
70
71   /* Read keys */
72   if (!read_rsa_key())
73     {
74       com_err(whoami, errno, "reading RSA key");
75       exit(1);
76     }
77   if (!read_hmac_key())
78     {
79       com_err(whoami, errno, "reading HMAC key");
80       exit(1);
81     }
82
83   /* Read error messages */
84   if (!read_errors())
85     {
86       com_err(whoami, errno, "reading error messages");
87       exit(1);
88     }
89
90   /* Connect to database */
91   EXEC SQL CONNECT :db IDENTIFIED BY :db;
92   if (sqlca.sqlcode)
93     {
94       char err_msg[256];
95       int bufsize = 256, msglength = 0;
96
97       sqlglm(err_msg, &bufsize, &msglength);
98       err_msg[msglength] = 0;
99       com_err(whoami, 0, "SQL error connecting to DBMS:\n%s", err_msg);
100       exit(1);
101     }
102
103   /* Get my hostname */
104   uname(&uts);
105   h = gethostbyname(uts.nodename);
106   if (!h)
107     {
108       com_err(whoami, 0, "Couldn't resolve hostname %s", uts.nodename);
109       exit(1);
110     }
111   hostname = lowercase(xstrdup(h->h_name));
112   shorthostname = xstrdup(hostname);
113   if (strchr(shorthostname, '.'))
114     *strchr(shorthostname, '.') = '\0';
115
116   /* Initialize kerberos */
117   status = init_kerberos();
118   if (status)
119     {
120       com_err(whoami, status, "initializing kerberos library");
121       exit(1);
122     }
123
124   /* Set up listening socket. */
125   listener = mr_listen("moira_ureg");
126   if (listener < 0)
127     {
128       com_err(whoami, errno, "couldn't create listening socket");
129       exit(1);
130     }
131   FD_ZERO(&xreadfds);
132   FD_SET(listener, &xreadfds);
133   nfds = listener + 1;
134
135   /* Initialize client array. */
136   nclients = 0;
137   clientssize = 5;
138   clients = malloc(clientssize * sizeof(reg_client));
139   if (!clients)
140     {
141       com_err(whoami, errno, "creating client array");
142       exit(1);
143     }
144
145   /* Set up signal handlers */
146   sa.sa_flags = 0;
147   sigemptyset(&sa.sa_mask);
148   sa.sa_handler = sigshut;
149   sigaction(SIGTERM, &sa, NULL);
150   sigaction(SIGINT, &sa, NULL);
151   sigaction(SIGHUP, &sa, NULL);
152   sa.sa_handler = SIG_IGN;
153   sigaction(SIGPIPE, &sa, NULL);
154
155   com_err(whoami, 0, "started (pid %d)", getpid());
156   com_err(whoami, 0, rcsid);
157
158   /* Main loop */
159   while (state != RS_EXITING)
160     {
161       if (state == RS_RUNNING && stat(MOIRA_MOTD_FILE, &st) == 0)
162         {
163           state = RS_SLEEPING;
164           com_err(whoami, 0, "found motd. reg_svr is sleeping");
165         }
166       else if (state == RS_SLEEPING && stat(MOIRA_MOTD_FILE, &st) == -1)
167         {
168           state = RS_RUNNING;
169           com_err(whoami, 0, "motd gone. reg_svr is running");
170         }
171
172       memcpy(&readfds, &xreadfds, sizeof(readfds));
173       if (select(nfds, &readfds, NULL, NULL, NULL) == -1)
174         {
175           if (errno != EINTR)
176             com_err(whoami, errno, "in select");
177           continue;
178         }
179
180       if (FD_ISSET(listener, &readfds))
181         {
182           int newconn, addrlen = sizeof(struct sockaddr_in);
183           struct sockaddr_in addr;
184
185           newconn = accept(listener, (struct sockaddr *)&addr, &addrlen);
186           if (newconn < 0)
187             com_err(whoami, errno, "accepting new connection");
188           else
189             {
190               nclients++;
191               if (nclients > clientssize)
192                 {
193                   clientssize = 2 * clientssize;
194                   clients = xrealloc(clients, clientssize *
195                                      sizeof(reg_client));
196                 }
197
198               cl = &clients[nclients - 1];
199               memset(cl, 0, sizeof(reg_client));
200               cl->fd = newconn;
201               cl->lastmod = time(NULL);
202               cl->clientid = ++clientid;
203               cl->random = init_rand(cl);
204               FD_SET(newconn, &xreadfds);
205               if (newconn >= nfds)
206                 nfds = newconn + 1;
207
208               com_err(whoami, 0,
209                       "New connection from %s port %d (now %d client%s)",
210                       inet_ntoa(addr.sin_addr), (int)ntohs(addr.sin_port),
211                       nclients, nclients != 1 ? "s" : "");
212             }
213         }
214
215       for (i = 0; i < nclients; i++)
216         {
217           cl = &clients[i];
218           if (FD_ISSET(cl->fd, &readfds))
219             {
220               cl->lastmod = time(NULL);
221               if (!cl->buf)
222                 {
223                   /* We're just starting */
224                   cl->buf = malloc(3);
225                   if (!cl->buf)
226                     {
227                       com_err(whoami, errno, "allocating read buffer");
228                       reply(cl, INTERNAL_ERROR, "INIT", "c", NULL,
229                             "Out of memory");
230                       goto reap;
231                     }
232                   cl->nread = 0;
233                 }
234
235               if (cl->nread < 3)
236                 {
237                   /* We haven't read the length byte yet... */
238                   cl->nread += read(cl->fd, cl->buf + cl->nread,
239                                     3 - cl->nread);
240                   if (cl->nread == 3)
241                     {
242                       cl->nmax = cl->buf[1] * 256 + cl->buf[2] + 3;
243                       cl->buf = realloc(cl->buf, cl->nmax + 3);
244                       if (!cl->buf)
245                         {
246                           com_err(whoami, errno, "reallocating read buffer");
247                           reply(cl, INTERNAL_ERROR, "INIT", "c", NULL,
248                                 "Out of memory");
249                           goto reap;
250                         }
251                     }
252                   else if (cl->nread == 0)
253                     {
254                       /* client has closed connection. setting
255                          lastmod will cause it to be reaped */
256                       cl->lastmod = 0;
257                     }
258                 }
259               else
260                 {
261                   /* We know how long the packet is supposed to be */
262                   cl->nread += read(cl->fd, cl->buf + cl->nread,
263                                     cl->nmax - cl->nread);
264                   if (cl->nread == cl->nmax)
265                     {
266                       parse_packet(cl, cl->buf[0], cl->nread - 3, cl->buf + 3,
267                                    state == RS_SLEEPING);
268                       free(cl->buf);
269                       cl->buf = NULL;
270                     }
271                 }
272             }
273
274         reap:
275           if (cl->lastmod < time(NULL) - TIMEOUT)
276             {
277               com_err(whoami, 0, "Closed connection. (now %d client%s)",
278                       nclients - 1, nclients != 2 ? "s" : "");
279               shutdown(cl->fd, 2);
280               close(cl->fd);
281               FD_CLR(cl->fd, &xreadfds);
282               free(cl->buf);
283               free(cl->id);
284               free(cl->username);
285               free(cl->suggestions);
286               free(cl->random);
287               clients[i] = clients[--nclients];
288               i--;
289             }
290         }
291       cl = NULL;
292     }
293   com_err(whoami, 0, "Exiting.");
294 }
295
296 void RIFO(reg_client *rc, int argc, char **argv)
297 {
298   EXEC SQL BEGIN DECLARE SECTION;
299   char *ufirst, *umiddle, *ulast, *id;
300   char login[USERS_LOGIN_SIZE], first[USERS_FIRST_SIZE];
301   char middle[USERS_MIDDLE_SIZE], last[USERS_LAST_SIZE];
302   char fullname[USERS_FIRST_SIZE + USERS_MIDDLE_SIZE + USERS_LAST_SIZE];
303   char class[USERS_TYPE_SIZE], pin[USERS_PIN_SIZE];
304   int uid, status, secure, sqlstatus, count;
305   EXEC SQL END DECLARE SECTION;
306
307   if (rc->uid || argc != 4)
308     {
309       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
310       return;
311     }
312
313   ufirst = argv[0];
314   umiddle = argv[1];
315   ulast = argv[2];
316   id = argv[3];
317
318   EXEC SQL SELECT count(login) INTO :count FROM users WHERE clearid = :id;
319
320   /* "ORDER BY status" so that if there's both a matching state 0 entry
321      and a matching state 3 entry, we'll get the former. */
322   EXEC SQL DECLARE csr_id CURSOR FOR
323     SELECT login, unix_uid, status, secure, pin, first, middle, last, type
324     FROM users WHERE clearid = :id ORDER BY status;
325   EXEC SQL OPEN csr_id;
326   while (1)
327     {
328       EXEC SQL FETCH csr_id INTO :login, :uid, :status,
329         :secure, :pin, :first, :middle, :last, :class;
330       if (sqlca.sqlcode)
331         break;
332       strtrim(login);
333       strtrim(first);
334       strtrim(middle);
335       strtrim(last);
336       strtrim(class);
337       strtrim(pin);
338
339       /* It's possible they have both a deleted account and a status 8
340        * account.  We can't compensate for that in the ORDER BY clause
341        * above, so check here.  If they have more than one entry and the
342        * first one we get is deleted, skip it.
343        */
344       if (status == US_DELETED && count > 1)
345         continue;
346
347       /* Check names, allowing for the possibility that Moira and the
348          user might have them split up differently. eg, Mary/Ann/Singleton
349          vs. Mary Ann/Singleton. */
350       if (strcasecmp(last, ulast) && strncasecmp(last, ulast, strlen(last)) &&
351           strncasecmp(last, ulast, strlen(ulast)))
352         continue;
353       if (strlen(last) > 3 && strlen(ulast) < 3)
354         continue;
355       if (strcasecmp(first, ufirst) &&
356           strncasecmp(first, ufirst, strlen(first)) &&
357           strncasecmp(first, ufirst, strlen(ufirst)))
358         continue;
359       if (strlen(first) > 3 && strlen(ufirst) < 3)
360         continue;
361       /* Ignore the middle name since Moira doesn't have those reliably */
362       break;
363     }
364   sqlstatus = sqlca.sqlcode;
365   EXEC SQL CLOSE csr_id;
366
367   if (sqlstatus)
368     {
369       reply(rc, NOT_FOUND_IN_DATABASE, "GETN", "d", NULL);
370       return;
371     }
372
373   switch (status)
374     {
375     case US_REGISTERED:
376     case US_ENROLLED:
377     case US_ENROLL_NOT_ALLOWED:
378     case US_REGISTERED_KERBEROS_ONLY:
379       reply(rc, ALREADY_REGISTERED, "INIT", "c", NULL, login);
380       return;
381
382     case US_DELETED:
383       reply(rc, ACCOUNT_DELETED, "INIT", "c", NULL, login);
384       return;
385
386     case US_NOT_ALLOWED:
387       reply(rc, NOT_ELIGIBLE, "INIT", "c", NULL);
388       return;
389
390     default:
391       break;
392     }
393
394   rc->user_status = status;
395   rc->uid = uid;
396   sprintf(fullname, "%s %s%s%s", first, middle, *middle ? " " : "", last);
397   if (!strcmp(class, "MITS"))
398     strcpy(class, "STAFF");
399   if (secure == 1)
400     {
401       rc->id = strdup(id);
402       if (!rc->id)
403         {
404           com_err(whoami, errno, "in RIFO");
405           reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
406           return;
407         }
408     }
409   if (*login != '#')
410     {
411       rc->reserved_username = 1;
412       rc->username = strdup(login);
413       if (!rc->username)
414         {
415           com_err(whoami, errno, "in RIFO");
416           reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
417           return;
418         }
419     }
420   else
421     {
422       rc->suggestions = find_usernames(first, middle, last);
423       if (!rc->suggestions && errno)
424         {
425           com_err(whoami, errno, "in RIFO");
426           reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(errno));
427           return;
428         }
429     }
430
431   if (rc->id)
432     {
433       if (*pin != '\0')
434         reply(rc, FOUND, "GETI", "c", NULL, fullname, class);
435       else
436         reply(rc, FOUND, "GETW", "c", NULL, fullname, class);
437     }
438   else if (!rc->username)
439     reply(rc, FOUND, "GETL", "c", rc->suggestions, fullname, class);
440   else
441     {
442       if (rc->user_status == US_NO_LOGIN_YET || 
443           rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
444         {
445           status = check_kerberos(login);
446           if (status == MR_SUCCESS && 
447               rc->user_status != US_NO_LOGIN_YET_KERBEROS_ONLY)
448             status = register_user(rc->uid, login);
449           if (status == MR_IN_USE)
450             {
451               reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL, 
452                     rc->username);
453               return;
454             }
455           else if (status == MR_DOWN)
456             {
457               reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
458               return;
459             }
460           else if (status != MR_SUCCESS)
461             {
462               reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, 
463                     error_message(status));
464               return;
465             }
466         }
467       reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
468     }
469 }
470
471 void SWRD(reg_client *rc, int argc, char **argv)
472 {
473   char *words[6];
474   int i;
475
476   if (!rc->id || argc != 6)
477     {
478       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
479       return;
480     }
481
482   getwordlist(rc->id, words);
483   for (i = 0; i < 6; i++)
484     {
485       if (strcasecmp(strtrim(argv[i]), words[i]))
486         break;
487     }
488   if (i != 6)
489     {
490       reply(rc, BAD_SIX_WORDS, "GETW", "d", NULL);
491       return;
492     }
493
494   free(rc->id);
495   rc->id = NULL;
496   if (!rc->username)
497     reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
498   else
499     reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
500 }
501
502 void SPIN(reg_client *rc, int argc, char **argv)
503 {
504   EXEC SQL BEGIN DECLARE SECTION;
505   char pin[USERS_PIN_SIZE];
506   EXEC SQL END DECLARE SECTION;
507
508   if (!rc->id || argc != 1)
509     {
510       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
511       return;
512     }
513
514   EXEC SQL SELECT pin INTO :pin FROM users WHERE clearid = :rc->id
515     AND status = :rc->user_status;
516   strtrim(pin);
517   if (strcmp(argv[0], pin) != 0)
518     {
519       reply(rc, BAD_PIN, "GETI", "d", NULL);
520       return;
521     }
522
523   free(rc->id);
524   rc->id = NULL;
525   if (!rc->username)
526     reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
527   else
528     {
529       register_user(rc->uid, rc->username);
530       reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
531     }
532 }
533
534 void LOGN(reg_client *rc, int argc, char **argv)
535 {
536   int i;
537   char *login;
538   long status;
539
540   if (!rc->uid || rc->id || rc->username || argc != 1)
541     {
542       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
543       return;
544     }
545
546   login = argv[0];
547
548   /* make sure someone's not trying to overrun reply */
549   if (strlen(login) > 100)
550     {
551       com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
552       rc->lastmod = 0;
553       return;
554     }
555
556   if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
557       (login[0] == '_') || isdigit(login[0]))
558     {
559       reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
560             3, USERS_LOGIN_SIZE - 1);
561       return;
562     }
563
564   for (i = 0; i < strlen(login); i++)
565     {
566       if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
567         {
568           reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
569                 3, USERS_LOGIN_SIZE - 1);
570           return;
571         }
572     }
573
574   status = check_kerberos(login);
575   if (status == MR_SUCCESS)
576     {
577       if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
578         EXEC SQL UPDATE users SET login = :login WHERE unix_uid = :rc->uid;
579       else
580         status = register_user(rc->uid, login);
581     }
582   if (status == MR_IN_USE)
583     {
584       if (rc->reserved_username)
585         {
586           reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
587                 rc->username);
588           return;
589         }
590       reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
591       return;
592     }
593   else if (status == MR_DOWN)
594     {
595       reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
596       return;
597     }
598   else if (status != MR_SUCCESS)
599     {
600       reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
601       return;
602     }
603
604   rc->username = strdup(login);
605   if (!rc->username)
606     {
607       com_err(whoami, errno, "in LOGN");
608       reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
609       return;
610     }
611   reply(rc, USERNAME_OK, "GETP", "c", NULL, login);
612 }
613
614 int ctypes[256] = {
615   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ - ^O */
616   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^P - ^_ */
617   1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* SPACE - / */
618   3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, /* 0 - ? */
619   2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* : - O */
620   4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, /* P - _ */
621   2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* ` - o */
622   5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, /* p - ^? */
623   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
624   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
625   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
626   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
627   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
628   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
629   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
630   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
631 };
632
633 void PSWD(reg_client *rc, int argc, char **argv)
634 {
635   long status;
636   char *password = argv[0], *p;
637   EXEC SQL BEGIN DECLARE SECTION;
638   char *login = rc->username;
639   EXEC SQL END DECLARE SECTION;
640
641   if (!rc->username || rc->id || argc != 1)
642     {
643       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
644       return;
645     }
646
647   /* password quality checking */
648   if (strlen(password) < 4)
649     {
650       reply(rc, PASSWORD_SHORT, "GETP", "c", NULL);
651       return;
652     }
653
654   if (strlen(password) < 7)
655     {
656       for (p = password + 1; *p; p++)
657         {
658           if (ctypes[*p] != ctypes[*(p - 1)])
659             break;
660         }
661       if (!*p)
662         {
663           reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
664           return;
665         }
666     }
667   
668   if (!strcasecmp(password, "GykoR-66") ||
669       !strcasecmp(password, "slaRooBey") ||
670       !strcasecmp(password, "krang-its") ||
671       !strcasecmp(password, "2HotPeetzas") ||
672       !strcasecmp(password, "ItzAGurl"))
673     {
674       reply(rc, PASSWORD_SAMPLE, "GETP", "c", NULL);
675       return;
676     }
677
678   status = register_kerberos(rc->username, password);
679   if (status == MR_QUALITY)
680     {
681       reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
682       return;
683     }
684   else if (status == MR_IN_USE)
685     {
686       reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
687             rc->username);
688       return;
689     }
690   else if (status)
691     {
692       com_err(whoami, status, "registering username with Kerberos");
693       reply(rc, KADM_ERROR, "INIT", "c", NULL, error_message(status));
694       return;
695     }
696   
697   if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
698     EXEC SQL UPDATE users SET status = 9 WHERE login = :login;
699   else
700     EXEC SQL UPDATE users SET status = 1 WHERE login = :login;
701   EXEC SQL COMMIT;
702
703   reply(rc, DONE, "INIT", "c", NULL, rc->username);
704 }
705
706 void QUIT(reg_client *rc, int argc, char **argv)
707 {
708 }
709
710 /* Register a user in Moira */
711 int register_user(int uid, char *username)
712 {
713   char uidbuf[10], *qargv[3], *motd = NULL;
714   long status;
715
716   status = mr_connect(hostname);
717   if (status)
718     return status;
719
720   status = mr_motd(&motd);
721   if (status || motd)
722     {
723       mr_disconnect();
724       return MR_DOWN;
725     }
726
727   status = krb_get_svc_in_tkt(REG_SVR_PRINCIPAL, REG_SVR_INSTANCE,
728                               krb_realmofhost(hostname), MOIRA_SNAME,
729                               shorthostname, 1, KEYFILE);
730   if (status)
731     status += ERROR_TABLE_BASE_krb;
732   else
733     status = mr_auth("reg_svr");
734   if (status)
735     {
736       com_err(whoami, status, "authenticating to moira");
737       mr_disconnect();
738       return MR_INTERNAL;
739     }
740
741   sprintf(uidbuf, "%d", uid);
742   qargv[0] = uidbuf;
743   qargv[1] = username;
744   qargv[2] = "IMAP";
745   status = mr_query("register_user", 3, qargv, NULL, NULL);
746   mr_disconnect();
747   return status;
748 }
749
750
751 /* Find some typical available usernames */
752
753 char *uname_patterns[] = {
754   "FL",         /* johndoe */
755   "fmllllll",   /* jmdoe... (last name truncated) */
756   "flllllll",   /* jdoe.... ("") */
757   "llllllll",   /* doe..... ("") */
758   "fml",        /* jmd */
759   "Fl",         /* johnd */
760   "Lf",         /* doej */
761   "Lfm",        /* doejm */
762   "F",          /* john */
763 };
764 int num_patterns = sizeof(uname_patterns) / sizeof(char *);
765
766 char *find_usernames(char *first, char *middle, char *last)
767 {
768   EXEC SQL BEGIN DECLARE SECTION;
769   char username[2 * USERS_LOGIN_SIZE];
770   int count;
771   EXEC SQL END DECLARE SECTION;
772   int pat, len;
773   char *pp, *up, *fp, *mp, *lp, *unames = NULL;
774
775   fixname(first);
776   fixname(middle);
777   fixname(last);
778
779   for (pat = 0; pat < num_patterns; pat++)
780     {
781       up = username;
782       fp = first;
783       mp = middle;
784       lp = last;
785       for (pp = uname_patterns[pat]; *pp; pp++)
786         {
787           switch (*pp)
788             {
789             case 'f':
790               if (*fp)
791                 *up++ = *fp++;
792               break;
793
794             case 'F':
795               if (up - username + strlen(first) < USERS_LOGIN_SIZE)
796                 up += sprintf(up, "%s", first);
797               else
798                 goto nextpattern;
799               break;
800
801             case 'm':
802               if (!*middle)
803                 goto nextpattern;
804               if (*mp)
805                 *up++ = *mp++;
806               break;
807
808             case 'l':
809               if (*lp)
810                 *up++ = *lp++;
811               break;
812
813             case 'L':
814               if (up - username + strlen(last) < USERS_LOGIN_SIZE)
815                 up += sprintf(up, "%s", last);
816               else
817                 goto nextpattern;
818               break;
819             }
820         }
821       *up = '\0';
822
823       if (strlen(username) < 3 || strlen(username) >= USERS_LOGIN_SIZE)
824         continue;
825
826       EXEC SQL SELECT COUNT(login) INTO :count FROM users
827         WHERE login = :username;
828       if (sqlca.sqlcode)
829         {
830           errno = MR_DBMS_ERR;
831           return NULL;
832         }
833       if (count == 0)
834         {
835           EXEC SQL SELECT COUNT(name) INTO :count FROM list
836             WHERE name = :username;
837           if (sqlca.sqlcode)
838             {
839               errno = MR_DBMS_ERR;
840               return NULL;
841             }
842         }
843       if (count == 0)
844         {
845           EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
846             WHERE label = :username;
847           if (sqlca.sqlcode)
848             {
849               errno = MR_DBMS_ERR;
850               return NULL;
851             }
852         }
853
854       if (count == 0)
855         {
856           if (unames)
857             {
858               unames = realloc(unames, strlen(unames) + strlen(username) + 3);
859               if (!unames)
860                 return NULL;
861               strcat(unames, ", ");
862               strcat(unames, username);
863             }
864           else
865             {
866               unames = strdup(username);
867               if (!unames)
868                 return NULL;
869             }
870         }
871
872     nextpattern:
873       ;
874     }
875
876   /* unames will be NULL if we couldn't suggest a username. Clear
877      errno so the caller can distinguish this from an error case. */
878   errno = 0;
879   return unames;
880 }
881
882 void fixname(char *name)
883 {
884   char *s, *d;
885
886   for (s = d = name; *s; s++)
887     {
888       if (isalnum(*s))
889         *d++ = tolower(*s);
890     }
891   *d = '\0';
892 }
893
894 void *xmalloc(size_t bytes)
895 {
896   void *buf = malloc(bytes);
897
898   if (buf)
899     return buf;
900
901   com_err(whoami, errno, "in xmalloc");
902   exit(1);
903 }
904
905 void *xrealloc(void *ptr, size_t bytes)
906 {
907   void *buf = realloc(ptr, bytes);
908
909   if (buf)
910     return buf;
911
912   com_err(whoami, errno, "in xrealloc");
913   exit(1);
914 }
915
916 char *xstrdup(char *str)
917 {
918   char *buf = strdup(str);
919
920   if (buf)
921     return buf;
922
923   com_err(whoami, errno, "in xstrdup");
924   exit(1);
925 }
926
927 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar)
928 {
929   if (whoami)
930     {
931       fputs(whoami, stderr);
932       if (cl)
933         fprintf(stderr, "[#%d]", cl->clientid);
934       fputs(": ", stderr);
935     }
936   if (code) {
937     fputs(error_message(code), stderr);
938     fputs(" ", stderr);
939   }
940   if (fmt)
941     vfprintf(stderr, fmt, pvar);
942   putc('\n', stderr);
943 }
944
945 void sigshut(int sig)
946 {
947   state = RS_EXITING;
948 }
This page took 0.133146 seconds and 5 git commands to generate.