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

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