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

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