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

Revision 23178, 24.2 KB checked in by broder, 16 years ago (diff)
Take a new snapshot from CVS for Moira, and add a debathena-moira-update-server package
Line 
1/* $Id$
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$");
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  char uidbuf[10], *qargv[3], *motd = NULL;
792  long status;
793
794  status = mr_connect(hostname);
795  if (status)
796    return status;
797
798  status = mr_motd(&motd);
799  if (status || motd)
800    {
801      mr_disconnect();
802      return MR_DOWN;
803    }
804
805  status = krb_get_svc_in_tkt(REG_SVR_PRINCIPAL, REG_SVR_INSTANCE,
806                              krb_realmofhost(hostname), MOIRA_SNAME,
807                              shorthostname, 3, KEYFILE);
808  if (status)
809    status += ERROR_TABLE_BASE_krb;
810  else
811    status = mr_krb5_auth("reg_svr");
812  if (status)
813    {
814      com_err(whoami, status, "authenticating to moira");
815      mr_disconnect();
816      return MR_INTERNAL;
817    }
818
819  sprintf(uidbuf, "%d", uid);
820  qargv[0] = uidbuf;
821  qargv[1] = username;
822  qargv[2] = "IMAP";
823  status = mr_query("register_user", 3, qargv, NULL, NULL);
824  mr_disconnect();
825  return status;
826}
827
828
829/* Find some typical available usernames */
830
831char *uname_patterns[] = {
832  "FL",         /* johndoe */
833  "fmllllll",   /* jmdoe... (last name truncated) */
834  "flllllll",   /* jdoe.... ("") */
835  "llllllll",   /* doe..... ("") */
836  "fml",        /* jmd */
837  "Fl",         /* johnd */
838  "Lf",         /* doej */
839  "Lfm",        /* doejm */
840  "F",          /* john */
841};
842int num_patterns = sizeof(uname_patterns) / sizeof(char *);
843
844char *find_usernames(char *first, char *middle, char *last)
845{
846  EXEC SQL BEGIN DECLARE SECTION;
847  char username[2 * USERS_LOGIN_SIZE];
848  int count;
849  EXEC SQL END DECLARE SECTION;
850  int pat, len;
851  char *pp, *up, *fp, *mp, *lp, *unames = NULL;
852
853  fixname(first);
854  fixname(middle);
855  fixname(last);
856
857  for (pat = 0; pat < num_patterns; pat++)
858    {
859      up = username;
860      fp = first;
861      mp = middle;
862      lp = last;
863      for (pp = uname_patterns[pat]; *pp; pp++)
864        {
865          switch (*pp)
866            {
867            case 'f':
868              if (*fp)
869                *up++ = *fp++;
870              break;
871
872            case 'F':
873              if (up - username + strlen(first) < USERS_LOGIN_SIZE)
874                up += sprintf(up, "%s", first);
875              else
876                goto nextpattern;
877              break;
878
879            case 'm':
880              if (!*middle)
881                goto nextpattern;
882              if (*mp)
883                *up++ = *mp++;
884              break;
885
886            case 'l':
887              if (*lp)
888                *up++ = *lp++;
889              break;
890
891            case 'L':
892              if (up - username + strlen(last) < USERS_LOGIN_SIZE)
893                up += sprintf(up, "%s", last);
894              else
895                goto nextpattern;
896              break;
897            }
898        }
899      *up = '\0';
900
901      if (strlen(username) < 3 || strlen(username) >= USERS_LOGIN_SIZE)
902        continue;
903
904      EXEC SQL SELECT COUNT(login) INTO :count FROM users
905        WHERE login = :username;
906      if (sqlca.sqlcode)
907        {
908          errno = MR_DBMS_ERR;
909          return NULL;
910        }
911      if (count == 0)
912        {
913          EXEC SQL SELECT COUNT(name) INTO :count FROM list
914            WHERE name = :username;
915          if (sqlca.sqlcode)
916            {
917              errno = MR_DBMS_ERR;
918              return NULL;
919            }
920        }
921      if (count == 0)
922        {
923          EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
924            WHERE label = :username;
925          if (sqlca.sqlcode)
926            {
927              errno = MR_DBMS_ERR;
928              return NULL;
929            }
930        }
931
932      if (count == 0)
933        {
934          if (unames)
935            {
936              unames = realloc(unames, strlen(unames) + strlen(username) + 3);
937              if (!unames)
938                return NULL;
939              strcat(unames, ", ");
940              strcat(unames, username);
941            }
942          else
943            {
944              unames = strdup(username);
945              if (!unames)
946                return NULL;
947            }
948        }
949
950    nextpattern:
951      ;
952    }
953
954  /* unames will be NULL if we couldn't suggest a username. Clear
955     errno so the caller can distinguish this from an error case. */
956  errno = 0;
957  return unames;
958}
959
960/* This does the database-side checks to make sure a username is
961 * available.
962 */
963int check_username_available(char *username)
964{
965  int count;
966
967  EXEC SQL SELECT COUNT(login) INTO :count FROM users
968    WHERE login = :username;
969  if (sqlca.sqlcode)
970    return MR_DBMS_ERR;
971  if (count != 0)
972    return MR_IN_USE;
973
974  EXEC SQL SELECT COUNT(name) INTO :count FROM list
975    WHERE name = :username;
976  if (sqlca.sqlcode)
977    return MR_DBMS_ERR;
978  if (count != 0)
979    return MR_IN_USE;
980
981  EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
982    WHERE label = :username;
983  if (sqlca.sqlcode)
984    return MR_DBMS_ERR;
985  if (count != 0)
986    return MR_IN_USE;
987
988  return MR_SUCCESS;
989}
990
991void fixname(char *name)
992{
993  char *s, *d;
994
995  for (s = d = name; *s; s++)
996    {
997      if (isalnum(*s))
998        *d++ = tolower(*s);
999    }
1000  *d = '\0';
1001}
1002
1003void *xmalloc(size_t bytes)
1004{
1005  void *buf = malloc(bytes);
1006
1007  if (buf)
1008    return buf;
1009
1010  com_err(whoami, errno, "in xmalloc");
1011  exit(1);
1012}
1013
1014void *xrealloc(void *ptr, size_t bytes)
1015{
1016  void *buf = realloc(ptr, bytes);
1017
1018  if (buf)
1019    return buf;
1020
1021  com_err(whoami, errno, "in xrealloc");
1022  exit(1);
1023}
1024
1025char *xstrdup(char *str)
1026{
1027  char *buf = strdup(str);
1028
1029  if (buf)
1030    return buf;
1031
1032  com_err(whoami, errno, "in xstrdup");
1033  exit(1);
1034}
1035
1036void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar)
1037{
1038  if (whoami)
1039    {
1040      fputs(whoami, stderr);
1041      if (cl)
1042        fprintf(stderr, "[#%d]", cl->clientid);
1043      fputs(": ", stderr);
1044    }
1045  if (code) {
1046    fputs(error_message(code), stderr);
1047    fputs(" ", stderr);
1048  }
1049  if (fmt)
1050    vfprintf(stderr, fmt, pvar);
1051  putc('\n', stderr);
1052}
1053
1054void sigshut(int sig)
1055{
1056  state = RS_EXITING;
1057}
Note: See TracBrowser for help on using the repository browser.