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

Revision 26024, 25.6 KB checked in by jdreed, 11 years ago (diff)
In moira: * Snapshot moira at r4113 to pick up new firewall-related changes
Line 
1/* $Id: reg_svr.pc 4113 2013-05-28 14:29:10Z 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 4113 2013-05-28 14:29:10Z 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    case US_EXPIRED:
390    case US_EXPIRED_KERBEROS_ONLY:
391      reply(rc, ALREADY_REGISTERED, "INIT", "c", NULL, login);
392      return;
393
394    case US_DELETED:
395      reply(rc, ACCOUNT_DELETED, "INIT", "c", NULL, login);
396      return;
397
398    case US_NOT_ALLOWED:
399      reply(rc, NOT_ELIGIBLE, "INIT", "c", NULL);
400      return;
401
402    default:
403      break;
404    }
405
406  rc->user_status = status;
407  rc->uid = uid;
408  sprintf(fullname, "%s %s%s%s", first, middle, *middle ? " " : "", last);
409  if (!strcmp(class, "MITS"))
410    strcpy(class, "STAFF");
411  if (secure == 1)
412    {
413      rc->id = strdup(id);
414      if (!rc->id)
415        {
416          com_err(whoami, errno, "in RIFO");
417          reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
418          return;
419        }
420    }
421  if (*login != '#')
422    {
423      rc->reserved_username = 1;
424      rc->username = strdup(login);
425      if (!rc->username)
426        {
427          com_err(whoami, errno, "in RIFO");
428          reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
429          return;
430        }
431    }
432  else
433    {
434      rc->suggestions = find_usernames(first, middle, last);
435      if (!rc->suggestions && errno)
436        {
437          com_err(whoami, errno, "in RIFO");
438          reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(errno));
439          return;
440        }
441    }
442
443  if (rc->id)
444    {
445      if (*pin != '\0')
446        reply(rc, FOUND, "GETI", "c", NULL, fullname, class);
447      else
448        reply(rc, FOUND, "GETW", "c", NULL, fullname, class);
449    }
450  else if (!rc->username)
451    reply(rc, FOUND, "GETL", "c", rc->suggestions, fullname, class);
452  else
453    {
454      if (rc->user_status == US_NO_LOGIN_YET ||
455          rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
456        {
457          status = check_kerberos(login);
458          if (status == MR_SUCCESS &&
459              rc->user_status != US_NO_LOGIN_YET_KERBEROS_ONLY)
460            status = register_user(rc->uid, login);
461          if (status == MR_IN_USE)
462            {
463              reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
464                    rc->username);
465              return;
466            }
467          else if (status == MR_DOWN)
468            {
469              reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
470              return;
471            }
472          else if (status != MR_SUCCESS)
473            {
474              reply(rc, INTERNAL_ERROR, "INIT", "c", NULL,
475                    error_message(status));
476              return;
477            }
478        }
479      reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
480    }
481}
482
483void SWRD(reg_client *rc, int argc, char **argv)
484{
485  char *words[6];
486  int i;
487
488  if (!rc->id || argc != 6)
489    {
490      reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
491      return;
492    }
493
494  getwordlist(rc->id, words);
495  for (i = 0; i < 6; i++)
496    {
497      if (strcasecmp(strtrim(argv[i]), words[i]))
498        break;
499    }
500  if (i != 6)
501    {
502      reply(rc, BAD_SIX_WORDS, "GETW", "d", NULL);
503      return;
504    }
505
506  free(rc->id);
507  rc->id = NULL;
508  if (!rc->username)
509    reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
510  else
511    reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
512}
513
514void SPIN(reg_client *rc, int argc, char **argv)
515{
516  EXEC SQL BEGIN DECLARE SECTION;
517  char pin[USERS_PIN_SIZE];
518  EXEC SQL END DECLARE SECTION;
519
520  if (!rc->id || argc != 1)
521    {
522      reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
523      return;
524    }
525
526  EXEC SQL SELECT pin INTO :pin FROM users WHERE clearid = :rc->id
527    AND status = :rc->user_status;
528  strtrim(pin);
529  if (strcmp(argv[0], pin) != 0)
530    {
531      reply(rc, BAD_PIN, "GETI", "d", NULL);
532      return;
533    }
534
535  free(rc->id);
536  rc->id = NULL;
537  if (!rc->username)
538    reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
539  else
540    {
541      register_user(rc->uid, rc->username);
542      reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
543    }
544}
545
546void CLGN(reg_client *rc, int argc, char **argv)
547{
548  int i;
549  char *login;
550  long status;
551
552  if (!rc->uid || rc->id || rc->username || argc != 1)
553    {
554      reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
555      return;
556    }
557
558  login = argv[0];
559 
560  /* make sure someone's not trying to overrun reply */
561  if (strlen(login) > 100)
562    {
563      com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
564      rc->lastmod = 0;
565      return;
566    }
567
568  if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
569      (login[0] == '_') || isdigit(login[0]))
570    {
571      reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
572            3, USERS_LOGIN_SIZE - 1);
573      return;
574    }
575
576  for (i = 0; i < strlen(login); i++)
577    {
578      if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
579        {
580          reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
581                3, USERS_LOGIN_SIZE - 1);
582          return;
583        }
584    }
585
586  status = check_kerberos(login);
587  if (status == MR_SUCCESS)
588    {
589      status = check_username_available(login);
590      if (status == MR_SUCCESS)
591        {
592          reply(rc, USERNAME_AVAILABLE, "LOGC", "c", login, login);
593          return;
594        }
595    }
596
597  if (status == MR_IN_USE)
598    {
599      if (rc->reserved_username)
600        {
601          reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
602                rc->username);
603          return;
604        }
605      reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
606      return;
607    }
608  else if (status == MR_DOWN)
609    {
610      reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
611      return;
612    }
613  else if (status != MR_SUCCESS)
614    {
615      reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
616      return;
617    }
618}
619
620void LOGN(reg_client *rc, int argc, char **argv)
621{
622  int i;
623  char *login;
624  long status;
625
626  if (!rc->uid || rc->id || rc->username || argc != 1)
627    {
628      reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
629      return;
630    }
631
632  login = argv[0];
633
634  /* make sure someone's not trying to overrun reply */
635  if (strlen(login) > 100)
636    {
637      com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
638      rc->lastmod = 0;
639      return;
640    }
641
642  if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
643      (login[0] == '_') || isdigit(login[0]))
644    {
645      reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
646            3, USERS_LOGIN_SIZE - 1);
647      return;
648    }
649
650  for (i = 0; i < strlen(login); i++)
651    {
652      if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
653        {
654          reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
655                3, USERS_LOGIN_SIZE - 1);
656          return;
657        }
658    }
659
660  status = check_kerberos(login);
661  if (status == MR_SUCCESS)
662    {
663      if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
664        {
665          EXEC SQL UPDATE users SET login = :login WHERE unix_uid = :rc->uid;
666          EXEC SQL COMMIT WORK;
667        }
668      else
669        status = register_user(rc->uid, login);
670    }
671  if (status == MR_IN_USE)
672    {
673      if (rc->reserved_username)
674        {
675          reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
676                rc->username);
677          return;
678        }
679      reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
680      return;
681    }
682  else if (status == MR_DOWN)
683    {
684      reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
685      return;
686    }
687  else if (status != MR_SUCCESS)
688    {
689      reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
690      return;
691    }
692
693  rc->username = strdup(login);
694  if (!rc->username)
695    {
696      com_err(whoami, errno, "in LOGN");
697      reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
698      return;
699    }
700  reply(rc, USERNAME_OK, "GETP", "c", NULL, login);
701}
702
703int ctypes[256] = {
704  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ - ^O */
705  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^P - ^_ */
706  1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* SPACE - / */
707  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, /* 0 - ? */
708  2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* : - O */
709  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, /* P - _ */
710  2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* ` - o */
711  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, /* p - ^? */
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  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
719  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
720};
721
722void PSWD(reg_client *rc, int argc, char **argv)
723{
724  long status;
725  char *password = argv[0], *p;
726  EXEC SQL BEGIN DECLARE SECTION;
727  char *login = rc->username;
728  EXEC SQL END DECLARE SECTION;
729
730  if (!rc->username || rc->id || argc != 1)
731    {
732      reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
733      return;
734    }
735
736  /* password quality checking */
737  if (strlen(password) < 4)
738    {
739      reply(rc, PASSWORD_SHORT, "GETP", "c", NULL);
740      return;
741    }
742
743  if (strlen(password) < 7)
744    {
745      for (p = password + 1; *p; p++)
746        {
747          if (ctypes[*p] != ctypes[*(p - 1)])
748            break;
749        }
750      if (!*p)
751        {
752          reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
753          return;
754        }
755    }
756 
757  if (!strcasecmp(password, "GykoR-66") ||
758      !strcasecmp(password, "slaRooBey") ||
759      !strcasecmp(password, "krang-its") ||
760      !strcasecmp(password, "2HotPeetzas") ||
761      !strcasecmp(password, "ItzAGurl"))
762    {
763      reply(rc, PASSWORD_SAMPLE, "GETP", "c", NULL);
764      return;
765    }
766
767  status = register_kerberos(rc->username, password);
768  if (status == MR_QUALITY)
769    {
770      reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
771      return;
772    }
773  else if (status == MR_IN_USE)
774    {
775      reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
776            rc->username);
777      return;
778    }
779  else if (status)
780    {
781      com_err(whoami, status, "registering username with Kerberos");
782      reply(rc, KADM_ERROR, "INIT", "c", NULL, error_message(status));
783      return;
784    }
785 
786  if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
787    status = update_user_status(rc->username, US_REGISTERED_KERBEROS_ONLY);
788  else
789    status = update_user_status(rc->username, US_REGISTERED);
790
791  if (status)
792    {
793      reply(rc, INTERNAL_ERROR, "INIT", "c", NULL,
794            error_message(status));
795      return;
796    }
797
798  reply(rc, DONE, "INIT", "c", NULL, rc->username, "http://owa.mit.edu");
799}
800
801void QUIT(reg_client *rc, int argc, char **argv)
802{
803}
804
805/* Update a user's status in Moira */
806int update_user_status(char *username, int account_status)
807{
808  char statusbuf[2], *qargv[2], *motd = NULL;
809  long status;
810
811  status = mr_connect(hostname);
812  if (status)
813    return status;
814
815  status = mr_motd(&motd);
816  if (status || motd)
817    {
818      mr_disconnect();
819      return MR_DOWN;
820    }
821
822  status = mr_krb5_auth("reg_svr");
823  if (status)
824    {
825      com_err(whoami, status, "authenticating to moira");
826      mr_disconnect();
827      return MR_INTERNAL;
828    }
829
830  sprintf(statusbuf, "%d", account_status);
831  qargv[0] = username;
832  qargv[1] = statusbuf;
833
834  status = mr_query("update_user_status", 2, qargv, NULL, NULL);
835  mr_disconnect();
836  return status;
837}
838
839/* Register a user in Moira */
840int register_user(int uid, char *username)
841{
842  char uidbuf[10], *qargv[3], *motd = NULL;
843  long status;
844
845  status = mr_connect(hostname);
846  if (status)
847    return status;
848
849  status = mr_motd(&motd);
850  if (status || motd)
851    {
852      mr_disconnect();
853      return MR_DOWN;
854    }
855
856  status = mr_krb5_auth("reg_svr");
857  if (status)
858    {
859      com_err(whoami, status, "authenticating to moira");
860      mr_disconnect();
861      return MR_INTERNAL;
862    }
863
864  sprintf(uidbuf, "%d", uid);
865  qargv[0] = uidbuf;
866  qargv[1] = username;
867  qargv[2] = "EXCHANGE";
868
869  status = mr_query("register_user", 3, qargv, NULL, NULL);
870  mr_disconnect();
871  return status;
872}
873
874
875/* Find some typical available usernames */
876
877char *uname_patterns[] = {
878  "FL",         /* johndoe */
879  "fmllllll",   /* jmdoe... (last name truncated) */
880  "flllllll",   /* jdoe.... ("") */
881  "llllllll",   /* doe..... ("") */
882  "fml",        /* jmd */
883  "Fl",         /* johnd */
884  "Lf",         /* doej */
885  "Lfm",        /* doejm */
886  "F",          /* john */
887};
888int num_patterns = sizeof(uname_patterns) / sizeof(char *);
889
890char *find_usernames(char *first, char *middle, char *last)
891{
892  EXEC SQL BEGIN DECLARE SECTION;
893  char username[2 * USERS_LOGIN_SIZE];
894  int count;
895  EXEC SQL END DECLARE SECTION;
896  int pat, len;
897  char *pp, *up, *fp, *mp, *lp, *unames = NULL;
898
899  fixname(first);
900  fixname(middle);
901  fixname(last);
902
903  for (pat = 0; pat < num_patterns; pat++)
904    {
905      up = username;
906      fp = first;
907      mp = middle;
908      lp = last;
909      for (pp = uname_patterns[pat]; *pp; pp++)
910        {
911          switch (*pp)
912            {
913            case 'f':
914              if (*fp)
915                *up++ = *fp++;
916              break;
917
918            case 'F':
919              if (up - username + strlen(first) < USERS_LOGIN_SIZE)
920                up += sprintf(up, "%s", first);
921              else
922                goto nextpattern;
923              break;
924
925            case 'm':
926              if (!*middle)
927                goto nextpattern;
928              if (*mp)
929                *up++ = *mp++;
930              break;
931
932            case 'l':
933              if (*lp)
934                *up++ = *lp++;
935              break;
936
937            case 'L':
938              if (up - username + strlen(last) < USERS_LOGIN_SIZE)
939                up += sprintf(up, "%s", last);
940              else
941                goto nextpattern;
942              break;
943            }
944        }
945      *up = '\0';
946
947      if (strlen(username) < 3 || strlen(username) >= USERS_LOGIN_SIZE)
948        continue;
949
950      EXEC SQL SELECT COUNT(login) INTO :count FROM users
951        WHERE login = :username;
952      if (sqlca.sqlcode)
953        {
954          errno = MR_DBMS_ERR;
955          return NULL;
956        }
957      if (count == 0)
958        {
959          EXEC SQL SELECT COUNT(name) INTO :count FROM list
960            WHERE name = :username;
961          if (sqlca.sqlcode)
962            {
963              errno = MR_DBMS_ERR;
964              return NULL;
965            }
966        }
967      if (count == 0)
968        {
969          EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
970            WHERE label = :username;
971          if (sqlca.sqlcode)
972            {
973              errno = MR_DBMS_ERR;
974              return NULL;
975            }
976        }
977
978      if (count == 0)
979        {
980          if (unames)
981            {
982              unames = realloc(unames, strlen(unames) + strlen(username) + 3);
983              if (!unames)
984                return NULL;
985              strcat(unames, ", ");
986              strcat(unames, username);
987            }
988          else
989            {
990              unames = strdup(username);
991              if (!unames)
992                return NULL;
993            }
994        }
995
996    nextpattern:
997      ;
998    }
999
1000  /* unames will be NULL if we couldn't suggest a username. Clear
1001     errno so the caller can distinguish this from an error case. */
1002  errno = 0;
1003  return unames;
1004}
1005
1006/* This does the database-side checks to make sure a username is
1007 * available.
1008 */
1009int check_username_available(char *username)
1010{
1011  int count;
1012
1013  EXEC SQL SELECT COUNT(login) INTO :count FROM users
1014    WHERE login = :username;
1015  if (sqlca.sqlcode)
1016    return MR_DBMS_ERR;
1017  if (count != 0)
1018    return MR_IN_USE;
1019
1020  EXEC SQL SELECT COUNT(name) INTO :count FROM list
1021    WHERE name = :username;
1022  if (sqlca.sqlcode)
1023    return MR_DBMS_ERR;
1024  if (count != 0)
1025    return MR_IN_USE;
1026
1027  EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
1028    WHERE label = :username;
1029  if (sqlca.sqlcode)
1030    return MR_DBMS_ERR;
1031  if (count != 0)
1032    return MR_IN_USE;
1033
1034  EXEC SQL SELECT COUNT(login) INTO :count FROM userhistory
1035    WHERE login = :username;
1036  if (sqlca.sqlcode)
1037    return MR_DBMS_ERR;
1038  if (count != 0)
1039    return MR_IN_USE;
1040
1041  return MR_SUCCESS;
1042}
1043
1044void fixname(char *name)
1045{
1046  char *s, *d;
1047
1048  for (s = d = name; *s; s++)
1049    {
1050      if (isalnum(*s))
1051        *d++ = tolower(*s);
1052    }
1053  *d = '\0';
1054}
1055
1056void *xmalloc(size_t bytes)
1057{
1058  void *buf = malloc(bytes);
1059
1060  if (buf)
1061    return buf;
1062
1063  com_err(whoami, errno, "in xmalloc");
1064  exit(1);
1065}
1066
1067void *xrealloc(void *ptr, size_t bytes)
1068{
1069  void *buf = realloc(ptr, bytes);
1070
1071  if (buf)
1072    return buf;
1073
1074  com_err(whoami, errno, "in xrealloc");
1075  exit(1);
1076}
1077
1078char *xstrdup(char *str)
1079{
1080  char *buf = strdup(str);
1081
1082  if (buf)
1083    return buf;
1084
1085  com_err(whoami, errno, "in xstrdup");
1086  exit(1);
1087}
1088
1089void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar)
1090{
1091  if (whoami)
1092    {
1093      fputs(whoami, stderr);
1094      if (cl)
1095        fprintf(stderr, "[#%d]", cl->clientid);
1096      fputs(": ", stderr);
1097    }
1098  if (code) {
1099    fputs(error_message(code), stderr);
1100    fputs(" ", stderr);
1101  }
1102  if (fmt)
1103    vfprintf(stderr, fmt, pvar);
1104  putc('\n', stderr);
1105}
1106
1107void sigshut(int sig)
1108{
1109  state = RS_EXITING;
1110}
1111
1112char *remove_char(char *str, int ch)
1113{
1114  char *dst = strchr(str, ch);
1115
1116  if (dst != NULL)
1117    {
1118      const char *src = dst;
1119      while (*dst != '\0')
1120        {
1121          while (*src == ch)
1122            src++;
1123          *dst++ = *src++;
1124        }
1125    }
1126  return str;
1127}
Note: See TracBrowser for help on using the repository browser.