]> andersk Git - moira.git/blob - reg_svr/reg_svr.pc
1016e18649431f7594cfcf918e4271890f1183f6
[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   EXEC SQL END DECLARE SECTION;
720
721   if (!rc->username || rc->id || argc != 1)
722     {
723       reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
724       return;
725     }
726
727   /* password quality checking */
728   if (strlen(password) < 4)
729     {
730       reply(rc, PASSWORD_SHORT, "GETP", "c", NULL);
731       return;
732     }
733
734   if (strlen(password) < 7)
735     {
736       for (p = password + 1; *p; p++)
737         {
738           if (ctypes[*p] != ctypes[*(p - 1)])
739             break;
740         }
741       if (!*p)
742         {
743           reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
744           return;
745         }
746     }
747   
748   if (!strcasecmp(password, "GykoR-66") ||
749       !strcasecmp(password, "slaRooBey") ||
750       !strcasecmp(password, "krang-its") ||
751       !strcasecmp(password, "2HotPeetzas") ||
752       !strcasecmp(password, "ItzAGurl"))
753     {
754       reply(rc, PASSWORD_SAMPLE, "GETP", "c", NULL);
755       return;
756     }
757
758   status = register_kerberos(rc->username, password);
759   if (status == MR_QUALITY)
760     {
761       reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
762       return;
763     }
764   else if (status == MR_IN_USE)
765     {
766       reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
767             rc->username);
768       return;
769     }
770   else if (status)
771     {
772       com_err(whoami, status, "registering username with Kerberos");
773       reply(rc, KADM_ERROR, "INIT", "c", NULL, error_message(status));
774       return;
775     }
776   
777   if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
778     EXEC SQL UPDATE users SET status = 9 WHERE login = :login;
779   else
780     EXEC SQL UPDATE users SET status = 1 WHERE login = :login;
781   EXEC SQL COMMIT;
782
783   reply(rc, DONE, "INIT", "c", NULL, rc->username);
784 }
785
786 void QUIT(reg_client *rc, int argc, char **argv)
787 {
788 }
789
790 /* Register a user in Moira */
791 int register_user(int uid, char *username)
792 {
793   EXEC SQL BEGIN DECLARE SECTION;
794   char class[USERS_TYPE_SIZE];
795   EXEC SQL END DECLARE SECTION;
796   char uidbuf[10], *qargv[3], *motd = NULL;
797   long status;
798
799   status = mr_connect(hostname);
800   if (status)
801     return status;
802
803   status = mr_motd(&motd);
804   if (status || motd)
805     {
806       mr_disconnect();
807       return MR_DOWN;
808     }
809
810   status = mr_krb5_auth("reg_svr");
811   if (status)
812     {
813       com_err(whoami, status, "authenticating to moira");
814       mr_disconnect();
815       return MR_INTERNAL;
816     }
817
818   EXEC SQL SELECT type INTO :class FROM users WHERE unix_uid = :uid;
819
820   sprintf(uidbuf, "%d", uid);
821   qargv[0] = uidbuf;
822   qargv[1] = username;
823
824   /* Incoming students should be given Exchange poboxes.
825    * Doesn't work for undergrads in the class of 2100 or higher.
826    */ 
827   if (!strcmp(strtrim(class), "G") || !strncmp(class, "FALL", 4) ||
828       !strncmp(class, "SPRING", 5) || !strncmp(class, "SUMMER", 6) ||
829       !strncmp(class, "20", 2))
830     {
831       com_err(whoami, 0, "assigning EXCHANGE pobox to user %s, class %s", username, class);
832       qargv[2] = "EXCHANGE";
833     }
834   else 
835     {
836       com_err(whoami, 0, "assigning IMAP pobox to user %s, class %s", username, class);
837       qargv[2] = "IMAP";
838     }
839
840   status = mr_query("register_user", 3, qargv, NULL, NULL);
841   mr_disconnect();
842   return status;
843 }
844
845
846 /* Find some typical available usernames */
847
848 char *uname_patterns[] = {
849   "FL",         /* johndoe */
850   "fmllllll",   /* jmdoe... (last name truncated) */
851   "flllllll",   /* jdoe.... ("") */
852   "llllllll",   /* doe..... ("") */
853   "fml",        /* jmd */
854   "Fl",         /* johnd */
855   "Lf",         /* doej */
856   "Lfm",        /* doejm */
857   "F",          /* john */
858 };
859 int num_patterns = sizeof(uname_patterns) / sizeof(char *);
860
861 char *find_usernames(char *first, char *middle, char *last)
862 {
863   EXEC SQL BEGIN DECLARE SECTION;
864   char username[2 * USERS_LOGIN_SIZE];
865   int count;
866   EXEC SQL END DECLARE SECTION;
867   int pat, len;
868   char *pp, *up, *fp, *mp, *lp, *unames = NULL;
869
870   fixname(first);
871   fixname(middle);
872   fixname(last);
873
874   for (pat = 0; pat < num_patterns; pat++)
875     {
876       up = username;
877       fp = first;
878       mp = middle;
879       lp = last;
880       for (pp = uname_patterns[pat]; *pp; pp++)
881         {
882           switch (*pp)
883             {
884             case 'f':
885               if (*fp)
886                 *up++ = *fp++;
887               break;
888
889             case 'F':
890               if (up - username + strlen(first) < USERS_LOGIN_SIZE)
891                 up += sprintf(up, "%s", first);
892               else
893                 goto nextpattern;
894               break;
895
896             case 'm':
897               if (!*middle)
898                 goto nextpattern;
899               if (*mp)
900                 *up++ = *mp++;
901               break;
902
903             case 'l':
904               if (*lp)
905                 *up++ = *lp++;
906               break;
907
908             case 'L':
909               if (up - username + strlen(last) < USERS_LOGIN_SIZE)
910                 up += sprintf(up, "%s", last);
911               else
912                 goto nextpattern;
913               break;
914             }
915         }
916       *up = '\0';
917
918       if (strlen(username) < 3 || strlen(username) >= USERS_LOGIN_SIZE)
919         continue;
920
921       EXEC SQL SELECT COUNT(login) INTO :count FROM users
922         WHERE login = :username;
923       if (sqlca.sqlcode)
924         {
925           errno = MR_DBMS_ERR;
926           return NULL;
927         }
928       if (count == 0)
929         {
930           EXEC SQL SELECT COUNT(name) INTO :count FROM list
931             WHERE name = :username;
932           if (sqlca.sqlcode)
933             {
934               errno = MR_DBMS_ERR;
935               return NULL;
936             }
937         }
938       if (count == 0)
939         {
940           EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
941             WHERE label = :username;
942           if (sqlca.sqlcode)
943             {
944               errno = MR_DBMS_ERR;
945               return NULL;
946             }
947         }
948
949       if (count == 0)
950         {
951           if (unames)
952             {
953               unames = realloc(unames, strlen(unames) + strlen(username) + 3);
954               if (!unames)
955                 return NULL;
956               strcat(unames, ", ");
957               strcat(unames, username);
958             }
959           else
960             {
961               unames = strdup(username);
962               if (!unames)
963                 return NULL;
964             }
965         }
966
967     nextpattern:
968       ;
969     }
970
971   /* unames will be NULL if we couldn't suggest a username. Clear
972      errno so the caller can distinguish this from an error case. */
973   errno = 0;
974   return unames;
975 }
976
977 /* This does the database-side checks to make sure a username is
978  * available.
979  */
980 int check_username_available(char *username)
981 {
982   int count;
983
984   EXEC SQL SELECT COUNT(login) INTO :count FROM users
985     WHERE login = :username;
986   if (sqlca.sqlcode)
987     return MR_DBMS_ERR;
988   if (count != 0)
989     return MR_IN_USE;
990
991   EXEC SQL SELECT COUNT(name) INTO :count FROM list
992     WHERE name = :username;
993   if (sqlca.sqlcode)
994     return MR_DBMS_ERR;
995   if (count != 0)
996     return MR_IN_USE;
997
998   EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
999     WHERE label = :username;
1000   if (sqlca.sqlcode)
1001     return MR_DBMS_ERR;
1002   if (count != 0)
1003     return MR_IN_USE;
1004
1005   EXEC SQL SELECT COUNT(login) INTO :count FROM userhistory
1006     WHERE login = :username;
1007   if (sqlca.sqlcode)
1008     return MR_DBMS_ERR;
1009   if (count != 0)
1010     return MR_IN_USE;
1011
1012   return MR_SUCCESS;
1013 }
1014
1015 void fixname(char *name)
1016 {
1017   char *s, *d;
1018
1019   for (s = d = name; *s; s++)
1020     {
1021       if (isalnum(*s))
1022         *d++ = tolower(*s);
1023     }
1024   *d = '\0';
1025 }
1026
1027 void *xmalloc(size_t bytes)
1028 {
1029   void *buf = malloc(bytes);
1030
1031   if (buf)
1032     return buf;
1033
1034   com_err(whoami, errno, "in xmalloc");
1035   exit(1);
1036 }
1037
1038 void *xrealloc(void *ptr, size_t bytes)
1039 {
1040   void *buf = realloc(ptr, bytes);
1041
1042   if (buf)
1043     return buf;
1044
1045   com_err(whoami, errno, "in xrealloc");
1046   exit(1);
1047 }
1048
1049 char *xstrdup(char *str)
1050 {
1051   char *buf = strdup(str);
1052
1053   if (buf)
1054     return buf;
1055
1056   com_err(whoami, errno, "in xstrdup");
1057   exit(1);
1058 }
1059
1060 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar)
1061 {
1062   if (whoami)
1063     {
1064       fputs(whoami, stderr);
1065       if (cl)
1066         fprintf(stderr, "[#%d]", cl->clientid);
1067       fputs(": ", stderr);
1068     }
1069   if (code) {
1070     fputs(error_message(code), stderr);
1071     fputs(" ", stderr);
1072   }
1073   if (fmt)
1074     vfprintf(stderr, fmt, pvar);
1075   putc('\n', stderr);
1076 }
1077
1078 void sigshut(int sig)
1079 {
1080   state = RS_EXITING;
1081 }
This page took 0.106695 seconds and 3 git commands to generate.