]> andersk Git - moira.git/blob - reg_svr/reg_svr.pc
504712f719752e0da3c2465d2e13c36e792d5050
[moira.git] / reg_svr / reg_svr.pc
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
38 EXEC SQL INCLUDE sqlca;
39
40 RCSID("$Header$");
41
42 char *whoami, *hostname, *shorthostname;
43
44 char *find_usernames(char *first, char *middle, char *last);
45 int check_username_available(char *username);
46 void fixname(char *name);
47 int register_user(int uid, char *username);
48 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar);
49 void sigshut(int);
50
51 reg_client *cl = NULL;
52 enum { RS_RUNNING, RS_SLEEPING, RS_EXITING } state = RS_RUNNING;
53
54 int 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
297 void 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
475 void 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
506 void 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
538 void 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
612 void 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
692 int 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
711 void 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
784 void QUIT(reg_client *rc, int argc, char **argv)
785 {
786 }
787
788 /* Register a user in Moira */
789 int 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
831 char *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 };
842 int num_patterns = sizeof(uname_patterns) / sizeof(char *);
843
844 char *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  */
963 int 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
991 void 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
1003 void *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
1014 void *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
1025 char *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
1036 void 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
1054 void sigshut(int sig)
1055 {
1056   state = RS_EXITING;
1057 }
This page took 0.107129 seconds and 3 git commands to generate.