]> andersk Git - moira.git/blob - reg_svr/reg_svr.pc
Assign EXCHANGE poboxes to all incoming students.
[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 class[USERS_TYPE_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 type INTO :class FROM users WHERE unix_uid = :uid;
823
824   sprintf(uidbuf, "%d", uid);
825   qargv[0] = uidbuf;
826   qargv[1] = username;
827
828   /* Incoming students should be given Exchange poboxes.
829    * Doesn't work for undergrads in the class of 2100 or higher.
830    */ 
831   if (!strcmp(strtrim(class), "G") || !strncmp(class, "FALL", 4) ||
832       !strncmp(class, "SPRING", 5) || !strncmp(class, "SUMMER", 6) ||
833       !strncmp(class, "20", 2))
834     {
835       com_err(whoami, 0, "assigning EXCHANGE pobox to user %s, class %s", username, class);
836       qargv[2] = "EXCHANGE";
837     }
838   else 
839     {
840       com_err(whoami, 0, "assigning IMAP pobox to user %s, class %s", username, class);
841       qargv[2] = "IMAP";
842     }
843
844   status = mr_query("register_user", 3, qargv, NULL, NULL);
845   mr_disconnect();
846   return status;
847 }
848
849
850 /* Find some typical available usernames */
851
852 char *uname_patterns[] = {
853   "FL",         /* johndoe */
854   "fmllllll",   /* jmdoe... (last name truncated) */
855   "flllllll",   /* jdoe.... ("") */
856   "llllllll",   /* doe..... ("") */
857   "fml",        /* jmd */
858   "Fl",         /* johnd */
859   "Lf",         /* doej */
860   "Lfm",        /* doejm */
861   "F",          /* john */
862 };
863 int num_patterns = sizeof(uname_patterns) / sizeof(char *);
864
865 char *find_usernames(char *first, char *middle, char *last)
866 {
867   EXEC SQL BEGIN DECLARE SECTION;
868   char username[2 * USERS_LOGIN_SIZE];
869   int count;
870   EXEC SQL END DECLARE SECTION;
871   int pat, len;
872   char *pp, *up, *fp, *mp, *lp, *unames = NULL;
873
874   fixname(first);
875   fixname(middle);
876   fixname(last);
877
878   for (pat = 0; pat < num_patterns; pat++)
879     {
880       up = username;
881       fp = first;
882       mp = middle;
883       lp = last;
884       for (pp = uname_patterns[pat]; *pp; pp++)
885         {
886           switch (*pp)
887             {
888             case 'f':
889               if (*fp)
890                 *up++ = *fp++;
891               break;
892
893             case 'F':
894               if (up - username + strlen(first) < USERS_LOGIN_SIZE)
895                 up += sprintf(up, "%s", first);
896               else
897                 goto nextpattern;
898               break;
899
900             case 'm':
901               if (!*middle)
902                 goto nextpattern;
903               if (*mp)
904                 *up++ = *mp++;
905               break;
906
907             case 'l':
908               if (*lp)
909                 *up++ = *lp++;
910               break;
911
912             case 'L':
913               if (up - username + strlen(last) < USERS_LOGIN_SIZE)
914                 up += sprintf(up, "%s", last);
915               else
916                 goto nextpattern;
917               break;
918             }
919         }
920       *up = '\0';
921
922       if (strlen(username) < 3 || strlen(username) >= USERS_LOGIN_SIZE)
923         continue;
924
925       EXEC SQL SELECT COUNT(login) INTO :count FROM users
926         WHERE login = :username;
927       if (sqlca.sqlcode)
928         {
929           errno = MR_DBMS_ERR;
930           return NULL;
931         }
932       if (count == 0)
933         {
934           EXEC SQL SELECT COUNT(name) INTO :count FROM list
935             WHERE name = :username;
936           if (sqlca.sqlcode)
937             {
938               errno = MR_DBMS_ERR;
939               return NULL;
940             }
941         }
942       if (count == 0)
943         {
944           EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
945             WHERE label = :username;
946           if (sqlca.sqlcode)
947             {
948               errno = MR_DBMS_ERR;
949               return NULL;
950             }
951         }
952
953       if (count == 0)
954         {
955           if (unames)
956             {
957               unames = realloc(unames, strlen(unames) + strlen(username) + 3);
958               if (!unames)
959                 return NULL;
960               strcat(unames, ", ");
961               strcat(unames, username);
962             }
963           else
964             {
965               unames = strdup(username);
966               if (!unames)
967                 return NULL;
968             }
969         }
970
971     nextpattern:
972       ;
973     }
974
975   /* unames will be NULL if we couldn't suggest a username. Clear
976      errno so the caller can distinguish this from an error case. */
977   errno = 0;
978   return unames;
979 }
980
981 /* This does the database-side checks to make sure a username is
982  * available.
983  */
984 int check_username_available(char *username)
985 {
986   int count;
987
988   EXEC SQL SELECT COUNT(login) INTO :count FROM users
989     WHERE login = :username;
990   if (sqlca.sqlcode)
991     return MR_DBMS_ERR;
992   if (count != 0)
993     return MR_IN_USE;
994
995   EXEC SQL SELECT COUNT(name) INTO :count FROM list
996     WHERE name = :username;
997   if (sqlca.sqlcode)
998     return MR_DBMS_ERR;
999   if (count != 0)
1000     return MR_IN_USE;
1001
1002   EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
1003     WHERE label = :username;
1004   if (sqlca.sqlcode)
1005     return MR_DBMS_ERR;
1006   if (count != 0)
1007     return MR_IN_USE;
1008
1009   return MR_SUCCESS;
1010 }
1011
1012 void fixname(char *name)
1013 {
1014   char *s, *d;
1015
1016   for (s = d = name; *s; s++)
1017     {
1018       if (isalnum(*s))
1019         *d++ = tolower(*s);
1020     }
1021   *d = '\0';
1022 }
1023
1024 void *xmalloc(size_t bytes)
1025 {
1026   void *buf = malloc(bytes);
1027
1028   if (buf)
1029     return buf;
1030
1031   com_err(whoami, errno, "in xmalloc");
1032   exit(1);
1033 }
1034
1035 void *xrealloc(void *ptr, size_t bytes)
1036 {
1037   void *buf = realloc(ptr, bytes);
1038
1039   if (buf)
1040     return buf;
1041
1042   com_err(whoami, errno, "in xrealloc");
1043   exit(1);
1044 }
1045
1046 char *xstrdup(char *str)
1047 {
1048   char *buf = strdup(str);
1049
1050   if (buf)
1051     return buf;
1052
1053   com_err(whoami, errno, "in xstrdup");
1054   exit(1);
1055 }
1056
1057 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar)
1058 {
1059   if (whoami)
1060     {
1061       fputs(whoami, stderr);
1062       if (cl)
1063         fprintf(stderr, "[#%d]", cl->clientid);
1064       fputs(": ", stderr);
1065     }
1066   if (code) {
1067     fputs(error_message(code), stderr);
1068     fputs(" ", stderr);
1069   }
1070   if (fmt)
1071     vfprintf(stderr, fmt, pvar);
1072   putc('\n', stderr);
1073 }
1074
1075 void sigshut(int sig)
1076 {
1077   state = RS_EXITING;
1078 }
This page took 0.128988 seconds and 5 git commands to generate.