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