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

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