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

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