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