source: trunk/third/moira/reg_svr/reg_svr.pc @ 23882

Revision 23882, 26.1 KB checked in by broder, 15 years ago (diff)
In moira: * New upstream release * Build and install libmoira as a shared library. (Trac: #70) * Drop most of the krb4 patch - it's been incorporated upstream. * Install the Moira development headers by patching the relevant Makefiles, instead of in the debian/rules file.
Line 
1/* $Id: reg_svr.pc,v 1.25 2009-06-19 02:18:47 zacheiss Exp $
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
40EXEC SQL INCLUDE sqlca;
41
42RCSID("$Header: /afs/athena.mit.edu/astaff/project/moiradev/repository/moira/reg_svr/reg_svr.pc,v 1.25 2009-06-19 02:18:47 zacheiss Exp $");
43
44char *whoami, *hostname, *shorthostname;
45
46char *find_usernames(char *first, char *middle, char *last);
47int check_username_available(char *username);
48void fixname(char *name);
49int register_user(int uid, char *username);
50int update_user_status(char *username, int account_status);
51void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar);
52void sigshut(int);
53
54reg_client *cl = NULL;
55enum { RS_RUNNING, RS_SLEEPING, RS_EXITING } state = RS_RUNNING;
56
57int 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
300void 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
478void 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
509void 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
541void 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
615void 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
695int 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
714void 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
798void QUIT(reg_client *rc, int argc, char **argv)
799{
800}
801
802/* Update a user's status in Moira */
803int 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 */
837int 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
894char *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};
905int num_patterns = sizeof(uname_patterns) / sizeof(char *);
906
907char *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 */
1026int 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
1061void 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
1073void *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
1084void *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
1095char *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
1106void 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
1124void sigshut(int sig)
1125{
1126  state = RS_EXITING;
1127}
Note: See TracBrowser for help on using the repository browser.