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