]> andersk Git - moira.git/blob - reg_svr/reg_svr.pc
Handle the case of a user using a PIN for preauthentication (instead of
[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;
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   /* "ORDER BY status" so that if there's both a matching state 0 entry
319      and a matching state 3 entry, we'll get the former. */
320   EXEC SQL DECLARE csr_id CURSOR FOR
321     SELECT login, unix_uid, status, secure, pin, first, middle, last, type
322     FROM users WHERE clearid = :id ORDER BY status;
323   EXEC SQL OPEN csr_id;
324   while (1)
325     {
326       EXEC SQL FETCH csr_id INTO :login, :uid, :status,
327         :secure, :pin, :first, :middle, :last, :class;
328       if (sqlca.sqlcode)
329         break;
330       strtrim(login);
331       strtrim(first);
332       strtrim(middle);
333       strtrim(last);
334       strtrim(class);
335       strtrim(pin);
336
337       /* Check names, allowing for the possibility that Moira and the
338          user might have them split up differently. eg, Mary/Ann/Singleton
339          vs. Mary Ann/Singleton. */
340       if (strcasecmp(last, ulast) && strncasecmp(last, ulast, strlen(last)) &&
341           strncasecmp(last, ulast, strlen(ulast)))
342         continue;
343       if (strlen(last) > 3 && strlen(ulast) < 3)
344         continue;
345       if (strcasecmp(first, ufirst) &&
346           strncasecmp(first, ufirst, strlen(first)) &&
347           strncasecmp(first, ufirst, strlen(ufirst)))
348         continue;
349       if (strlen(first) > 3 && strlen(ufirst) < 3)
350         continue;
351       /* Ignore the middle name since Moira doesn't have those reliably */
352       break;
353     }
354   sqlstatus = sqlca.sqlcode;
355   EXEC SQL CLOSE csr_id;
356
357   if (sqlstatus)
358     {
359       reply(rc, NOT_FOUND_IN_DATABASE, "GETN", "d", NULL);
360       return;
361     }
362
363   switch (status)
364     {
365     case US_REGISTERED:
366     case US_ENROLLED:
367     case US_ENROLL_NOT_ALLOWED:
368     case US_REGISTERED_KERBEROS_ONLY:
369       reply(rc, ALREADY_REGISTERED, "INIT", "c", NULL, login);
370       return;
371
372     case US_DELETED:
373       reply(rc, ACCOUNT_DELETED, "INIT", "c", NULL, login);
374       return;
375
376     case US_NOT_ALLOWED:
377       reply(rc, NOT_ELIGIBLE, "INIT", "c", NULL);
378       return;
379
380     default:
381       break;
382     }
383
384   rc->user_status = status;
385   rc->uid = uid;
386   sprintf(fullname, "%s %s%s%s", first, middle, *middle ? " " : "", last);
387   if (!strcmp(class, "MITS"))
388     strcpy(class, "STAFF");
389   if (secure == 1)
390     {
391       rc->id = strdup(id);
392       if (!rc->id)
393         {
394           com_err(whoami, errno, "in RIFO");
395           reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
396           return;
397         }
398     }
399   if (*login != '#')
400     {
401       rc->reserved_username = 1;
402       rc->username = strdup(login);
403       if (!rc->username)
404         {
405           com_err(whoami, errno, "in RIFO");
406           reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
407           return;
408         }
409     }
410   else
411     {
412       rc->suggestions = find_usernames(first, middle, last);
413       if (!rc->suggestions && errno)
414         {
415           com_err(whoami, errno, "in RIFO");
416           reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(errno));
417           return;
418         }
419     }
420
421   if (rc->id)
422     {
423       if (*pin != '\0')
424         reply(rc, FOUND, "GETI", "c", NULL, fullname, class);
425       else
426         reply(rc, FOUND, "GETW", "c", NULL, fullname, class);
427     }
428   else if (!rc->username)
429     reply(rc, FOUND, "GETL", "c", rc->suggestions, fullname, class);
430   else
431     {
432       if (rc->user_status == US_NO_LOGIN_YET || 
433           rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
434         {
435           status = check_kerberos(login);
436           if (status == MR_SUCCESS && 
437               rc->user_status != US_NO_LOGIN_YET_KERBEROS_ONLY)
438             status = register_user(rc->uid, login);
439           if (status == MR_IN_USE)
440             {
441               reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL, 
442                     rc->username);
443               return;
444             }
445           else if (status == MR_DOWN)
446             {
447               reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
448               return;
449             }
450           else if (status != MR_SUCCESS)
451             {
452               reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, 
453                     error_message(status));
454               return;
455             }
456         }
457       reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
458     }
459 }
460
461 void SWRD(reg_client *rc, int argc, char **argv)
462 {
463   char *words[6];
464   int i;
465
466   if (!rc->id || argc != 6)
467     {
468       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
469       return;
470     }
471
472   getwordlist(rc->id, words);
473   for (i = 0; i < 6; i++)
474     {
475       if (strcasecmp(strtrim(argv[i]), words[i]))
476         break;
477     }
478   if (i != 6)
479     {
480       reply(rc, BAD_SIX_WORDS, "GETW", "d", NULL);
481       return;
482     }
483
484   free(rc->id);
485   rc->id = NULL;
486   if (!rc->username)
487     reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
488   else
489     reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
490 }
491
492 void SPIN(reg_client *rc, int argc, char **argv)
493 {
494   EXEC SQL BEGIN DECLARE SECTION;
495   char pin[USERS_PIN_SIZE];
496   EXEC SQL END DECLARE SECTION;
497
498   if (!rc->id || argc != 1)
499     {
500       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
501       return;
502     }
503
504   /* "ORDER BY status" so that if there's both a matching state 0 entry
505      and a matching state 3 entry, we'll get the former. */
506   EXEC SQL SELECT pin INTO :pin FROM users WHERE clearid = :rc->id
507     ORDER BY status;
508   strtrim(pin);
509   if (strcmp(argv[0], pin) != 0)
510     {
511       reply(rc, BAD_PIN, "GETI", "d", NULL);
512       return;
513     }
514
515   free(rc->id);
516   rc->id = NULL;
517   if (!rc->username)
518     reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
519   else
520     {
521       register_user(rc->uid, rc->username);
522       reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
523     }
524 }
525
526 void LOGN(reg_client *rc, int argc, char **argv)
527 {
528   int i;
529   char *login;
530   long status;
531
532   if (!rc->uid || rc->id || rc->username || argc != 1)
533     {
534       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
535       return;
536     }
537
538   login = argv[0];
539
540   /* make sure someone's not trying to overrun reply */
541   if (strlen(login) > 100)
542     {
543       com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
544       rc->lastmod = 0;
545       return;
546     }
547
548   if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
549       (login[0] == '_') || isdigit(login[0]))
550     {
551       reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
552             3, USERS_LOGIN_SIZE - 1);
553       return;
554     }
555
556   for (i = 0; i < strlen(login); i++)
557     {
558       if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
559         {
560           reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
561                 3, USERS_LOGIN_SIZE - 1);
562           return;
563         }
564     }
565
566   status = check_kerberos(login);
567   if (status == MR_SUCCESS)
568     {
569       if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
570         EXEC SQL UPDATE users SET login = :login WHERE unix_uid = :rc->uid;
571       else
572         status = register_user(rc->uid, login);
573     }
574   if (status == MR_IN_USE)
575     {
576       if (rc->reserved_username)
577         {
578           reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
579                 rc->username);
580           return;
581         }
582       reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
583       return;
584     }
585   else if (status == MR_DOWN)
586     {
587       reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
588       return;
589     }
590   else if (status != MR_SUCCESS)
591     {
592       reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
593       return;
594     }
595
596   rc->username = strdup(login);
597   if (!rc->username)
598     {
599       com_err(whoami, errno, "in LOGN");
600       reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
601       return;
602     }
603   reply(rc, USERNAME_OK, "GETP", "c", NULL, login);
604 }
605
606 int ctypes[256] = {
607   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ - ^O */
608   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^P - ^_ */
609   1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* SPACE - / */
610   3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, /* 0 - ? */
611   2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* : - O */
612   4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, /* P - _ */
613   2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* ` - o */
614   5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, /* p - ^? */
615   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
616   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
617   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
618   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
619   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
620   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
621   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
622   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
623 };
624
625 void PSWD(reg_client *rc, int argc, char **argv)
626 {
627   long status;
628   char *password = argv[0], *p;
629   EXEC SQL BEGIN DECLARE SECTION;
630   char *login = rc->username;
631   EXEC SQL END DECLARE SECTION;
632
633   if (!rc->username || rc->id || argc != 1)
634     {
635       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
636       return;
637     }
638
639   /* password quality checking */
640   if (strlen(password) < 4)
641     {
642       reply(rc, PASSWORD_SHORT, "GETP", "c", NULL);
643       return;
644     }
645
646   if (strlen(password) < 7)
647     {
648       for (p = password + 1; *p; p++)
649         {
650           if (ctypes[*p] != ctypes[*(p - 1)])
651             break;
652         }
653       if (!*p)
654         {
655           reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
656           return;
657         }
658     }
659   
660   if (!strcasecmp(password, "GykoR-66") ||
661       !strcasecmp(password, "slaRooBey") ||
662       !strcasecmp(password, "krang-its") ||
663       !strcasecmp(password, "2HotPeetzas") ||
664       !strcasecmp(password, "ItzAGurl"))
665     {
666       reply(rc, PASSWORD_SAMPLE, "GETP", "c", NULL);
667       return;
668     }
669
670   status = register_kerberos(rc->username, password);
671   if (status == MR_QUALITY)
672     {
673       reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
674       return;
675     }
676   else if (status == MR_IN_USE)
677     {
678       reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
679             rc->username);
680       return;
681     }
682   else if (status)
683     {
684       com_err(whoami, status, "registering username with Kerberos");
685       reply(rc, KADM_ERROR, "INIT", "c", NULL, error_message(status));
686       return;
687     }
688   
689   if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
690     EXEC SQL UPDATE users SET status = 9 WHERE login = :login;
691   else
692     EXEC SQL UPDATE users SET status = 1 WHERE login = :login;
693   EXEC SQL COMMIT;
694
695   reply(rc, DONE, "INIT", "c", NULL, rc->username);
696 }
697
698 void QUIT(reg_client *rc, int argc, char **argv)
699 {
700 }
701
702 /* Register a user in Moira */
703 int register_user(int uid, char *username)
704 {
705   char uidbuf[10], *qargv[3], *motd = NULL;
706   long status;
707
708   status = mr_connect(hostname);
709   if (status)
710     return status;
711
712   status = mr_motd(&motd);
713   if (status || motd)
714     {
715       mr_disconnect();
716       return MR_DOWN;
717     }
718
719   status = krb_get_svc_in_tkt(REG_SVR_PRINCIPAL, REG_SVR_INSTANCE,
720                               krb_realmofhost(hostname), MOIRA_SNAME,
721                               shorthostname, 1, KEYFILE);
722   if (status)
723     status += ERROR_TABLE_BASE_krb;
724   else
725     status = mr_auth("reg_svr");
726   if (status)
727     {
728       com_err(whoami, status, "authenticating to moira");
729       mr_disconnect();
730       return MR_INTERNAL;
731     }
732
733   sprintf(uidbuf, "%d", uid);
734   qargv[0] = uidbuf;
735   qargv[1] = username;
736   qargv[2] = "IMAP";
737   status = mr_query("register_user", 3, qargv, NULL, NULL);
738   mr_disconnect();
739   return status;
740 }
741
742
743 /* Find some typical available usernames */
744
745 char *uname_patterns[] = {
746   "FL",         /* johndoe */
747   "fmllllll",   /* jmdoe... (last name truncated) */
748   "flllllll",   /* jdoe.... ("") */
749   "llllllll",   /* doe..... ("") */
750   "fml",        /* jmd */
751   "Fl",         /* johnd */
752   "Lf",         /* doej */
753   "Lfm",        /* doejm */
754   "F",          /* john */
755 };
756 int num_patterns = sizeof(uname_patterns) / sizeof(char *);
757
758 char *find_usernames(char *first, char *middle, char *last)
759 {
760   EXEC SQL BEGIN DECLARE SECTION;
761   char username[2 * USERS_LOGIN_SIZE];
762   int count;
763   EXEC SQL END DECLARE SECTION;
764   int pat, len;
765   char *pp, *up, *fp, *mp, *lp, *unames = NULL;
766
767   fixname(first);
768   fixname(middle);
769   fixname(last);
770
771   for (pat = 0; pat < num_patterns; pat++)
772     {
773       up = username;
774       fp = first;
775       mp = middle;
776       lp = last;
777       for (pp = uname_patterns[pat]; *pp; pp++)
778         {
779           switch (*pp)
780             {
781             case 'f':
782               if (*fp)
783                 *up++ = *fp++;
784               break;
785
786             case 'F':
787               if (up - username + strlen(first) < USERS_LOGIN_SIZE)
788                 up += sprintf(up, "%s", first);
789               else
790                 goto nextpattern;
791               break;
792
793             case 'm':
794               if (!*middle)
795                 goto nextpattern;
796               if (*mp)
797                 *up++ = *mp++;
798               break;
799
800             case 'l':
801               if (*lp)
802                 *up++ = *lp++;
803               break;
804
805             case 'L':
806               if (up - username + strlen(last) < USERS_LOGIN_SIZE)
807                 up += sprintf(up, "%s", last);
808               else
809                 goto nextpattern;
810               break;
811             }
812         }
813       *up = '\0';
814
815       if (strlen(username) < 3 || strlen(username) >= USERS_LOGIN_SIZE)
816         continue;
817
818       EXEC SQL SELECT COUNT(login) INTO :count FROM users
819         WHERE login = :username;
820       if (sqlca.sqlcode)
821         {
822           errno = MR_DBMS_ERR;
823           return NULL;
824         }
825       if (count == 0)
826         {
827           EXEC SQL SELECT COUNT(name) INTO :count FROM list
828             WHERE name = :username;
829           if (sqlca.sqlcode)
830             {
831               errno = MR_DBMS_ERR;
832               return NULL;
833             }
834         }
835       if (count == 0)
836         {
837           EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
838             WHERE label = :username;
839           if (sqlca.sqlcode)
840             {
841               errno = MR_DBMS_ERR;
842               return NULL;
843             }
844         }
845
846       if (count == 0)
847         {
848           if (unames)
849             {
850               unames = realloc(unames, strlen(unames) + strlen(username) + 3);
851               if (!unames)
852                 return NULL;
853               strcat(unames, ", ");
854               strcat(unames, username);
855             }
856           else
857             {
858               unames = strdup(username);
859               if (!unames)
860                 return NULL;
861             }
862         }
863
864     nextpattern:
865       ;
866     }
867
868   /* unames will be NULL if we couldn't suggest a username. Clear
869      errno so the caller can distinguish this from an error case. */
870   errno = 0;
871   return unames;
872 }
873
874 void fixname(char *name)
875 {
876   char *s, *d;
877
878   for (s = d = name; *s; s++)
879     {
880       if (isalnum(*s))
881         *d++ = tolower(*s);
882     }
883   *d = '\0';
884 }
885
886 void *xmalloc(size_t bytes)
887 {
888   void *buf = malloc(bytes);
889
890   if (buf)
891     return buf;
892
893   com_err(whoami, errno, "in xmalloc");
894   exit(1);
895 }
896
897 void *xrealloc(void *ptr, size_t bytes)
898 {
899   void *buf = realloc(ptr, bytes);
900
901   if (buf)
902     return buf;
903
904   com_err(whoami, errno, "in xrealloc");
905   exit(1);
906 }
907
908 char *xstrdup(char *str)
909 {
910   char *buf = strdup(str);
911
912   if (buf)
913     return buf;
914
915   com_err(whoami, errno, "in xstrdup");
916   exit(1);
917 }
918
919 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar)
920 {
921   if (whoami)
922     {
923       fputs(whoami, stderr);
924       if (cl)
925         fprintf(stderr, "[#%d]", cl->clientid);
926       fputs(": ", stderr);
927     }
928   if (code) {
929     fputs(error_message(code), stderr);
930     fputs(" ", stderr);
931   }
932   if (fmt)
933     vfprintf(stderr, fmt, pvar);
934   putc('\n', stderr);
935 }
936
937 void sigshut(int sig)
938 {
939   state = RS_EXITING;
940 }
This page took 0.338392 seconds and 5 git commands to generate.