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

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