3 * Server for user registration with Moira and Kerberos.
5 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
10 #include <mit-copyright.h>
12 #include <mr_private.h>
13 #include <moira_schema.h>
14 #include <moira_site.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
21 #include <sys/utsname.h>
23 #include <netinet/in.h>
38 EXEC SQL INCLUDE sqlca;
42 char *whoami, *hostname, *shorthostname;
44 char *find_usernames(char *first, char *middle, char *last);
45 void fixname(char *name);
46 int register_user(int uid, char *username);
47 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar);
50 reg_client *cl = NULL;
51 enum { RS_RUNNING, RS_SLEEPING, RS_EXITING } state = RS_RUNNING;
53 int main(int argc, char **argv)
55 int listener, nfds, i, clientid = 0;
56 fd_set readfds, xreadfds;
58 int nclients, clientssize;
66 whoami = strrchr(argv[0], '/');
67 whoami = whoami ? whoami + 1 : argv[0];
69 set_com_err_hook(mr_com_err);
74 com_err(whoami, errno, "reading RSA key");
79 com_err(whoami, errno, "reading HMAC key");
83 /* Read error messages */
86 com_err(whoami, errno, "reading error messages");
90 /* Connect to database */
91 EXEC SQL CONNECT :db IDENTIFIED BY :db;
95 int bufsize = 256, msglength = 0;
97 sqlglm(err_msg, &bufsize, &msglength);
98 err_msg[msglength] = 0;
99 com_err(whoami, 0, "SQL error connecting to DBMS:\n%s", err_msg);
103 /* Get my hostname */
105 h = gethostbyname(uts.nodename);
108 com_err(whoami, 0, "Couldn't resolve hostname %s", uts.nodename);
111 hostname = lowercase(xstrdup(h->h_name));
112 shorthostname = xstrdup(hostname);
113 if (strchr(shorthostname, '.'))
114 *strchr(shorthostname, '.') = '\0';
116 /* Initialize kerberos */
117 status = init_kerberos();
120 com_err(whoami, status, "initializing kerberos library");
124 /* Set up listening socket. */
125 listener = mr_listen("moira_ureg");
128 com_err(whoami, errno, "couldn't create listening socket");
132 FD_SET(listener, &xreadfds);
135 /* Initialize client array. */
138 clients = malloc(clientssize * sizeof(reg_client));
141 com_err(whoami, errno, "creating client array");
145 /* Set up signal handlers */
147 sigemptyset(&sa.sa_mask);
148 sa.sa_handler = sigshut;
149 sigaction(SIGTERM, &sa, NULL);
150 sigaction(SIGINT, &sa, NULL);
151 sigaction(SIGHUP, &sa, NULL);
152 sa.sa_handler = SIG_IGN;
153 sigaction(SIGPIPE, &sa, NULL);
155 com_err(whoami, 0, "started (pid %d)", getpid());
156 com_err(whoami, 0, rcsid);
159 while (state != RS_EXITING)
161 if (state == RS_RUNNING && stat(MOIRA_MOTD_FILE, &st) == 0)
164 com_err(whoami, 0, "found motd. reg_svr is sleeping");
166 else if (state == RS_SLEEPING && stat(MOIRA_MOTD_FILE, &st) == -1)
169 com_err(whoami, 0, "motd gone. reg_svr is running");
172 memcpy(&readfds, &xreadfds, sizeof(readfds));
173 if (select(nfds, &readfds, NULL, NULL, NULL) == -1)
176 com_err(whoami, errno, "in select");
180 if (FD_ISSET(listener, &readfds))
182 int newconn, addrlen = sizeof(struct sockaddr_in);
183 struct sockaddr_in addr;
185 newconn = accept(listener, (struct sockaddr *)&addr, &addrlen);
187 com_err(whoami, errno, "accepting new connection");
191 if (nclients > clientssize)
193 clientssize = 2 * clientssize;
194 clients = xrealloc(clients, clientssize *
198 cl = &clients[nclients - 1];
199 memset(cl, 0, sizeof(reg_client));
201 cl->lastmod = time(NULL);
202 cl->clientid = ++clientid;
203 cl->random = init_rand(cl);
204 FD_SET(newconn, &xreadfds);
209 "New connection from %s port %d (now %d client%s)",
210 inet_ntoa(addr.sin_addr), (int)ntohs(addr.sin_port),
211 nclients, nclients != 1 ? "s" : "");
215 for (i = 0; i < nclients; i++)
218 if (FD_ISSET(cl->fd, &readfds))
220 cl->lastmod = time(NULL);
223 /* We're just starting */
227 com_err(whoami, errno, "allocating read buffer");
228 reply(cl, INTERNAL_ERROR, "INIT", "c", NULL,
237 /* We haven't read the length byte yet... */
238 cl->nread += read(cl->fd, cl->buf + cl->nread,
242 cl->nmax = cl->buf[1] * 256 + cl->buf[2] + 3;
243 cl->buf = realloc(cl->buf, cl->nmax + 3);
246 com_err(whoami, errno, "reallocating read buffer");
247 reply(cl, INTERNAL_ERROR, "INIT", "c", NULL,
252 else if (cl->nread == 0)
254 /* client has closed connection. setting
255 lastmod will cause it to be reaped */
261 /* We know how long the packet is supposed to be */
262 cl->nread += read(cl->fd, cl->buf + cl->nread,
263 cl->nmax - cl->nread);
264 if (cl->nread == cl->nmax)
266 parse_packet(cl, cl->buf[0], cl->nread - 3, cl->buf + 3,
267 state == RS_SLEEPING);
275 if (cl->lastmod < time(NULL) - TIMEOUT)
277 com_err(whoami, 0, "Closed connection. (now %d client%s)",
278 nclients - 1, nclients != 2 ? "s" : "");
281 FD_CLR(cl->fd, &xreadfds);
285 free(cl->suggestions);
287 clients[i] = clients[--nclients];
293 com_err(whoami, 0, "Exiting.");
296 void RIFO(reg_client *rc, int argc, char **argv)
298 EXEC SQL BEGIN DECLARE SECTION;
299 char *ufirst, *umiddle, *ulast, *id;
300 char login[USERS_LOGIN_SIZE], first[USERS_FIRST_SIZE];
301 char middle[USERS_MIDDLE_SIZE], last[USERS_LAST_SIZE];
302 char fullname[USERS_FIRST_SIZE + USERS_MIDDLE_SIZE + USERS_LAST_SIZE];
303 char class[USERS_TYPE_SIZE];
304 int uid, status, secure, sqlstatus, string_id;
305 EXEC SQL END DECLARE SECTION;
307 if (rc->uid || argc != 4)
309 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
318 /* "ORDER BY status" so that if there's both a matching state 0 entry
319 and a matching state 3 entry, we'll get the former. */
320 EXEC SQL DECLARE csr_id CURSOR FOR
321 SELECT login, unix_uid, status, secure, first, middle, last, type
322 FROM users WHERE clearid = :id ORDER BY status;
323 EXEC SQL OPEN csr_id;
326 EXEC SQL FETCH csr_id INTO :login, :uid, :status,
327 :secure, :first, :middle, :last, :class;
336 /* Check names, allowing for the possibility that Moira and the
337 user might have them split up differently. eg, Mary/Ann/Singleton
338 vs. Mary Ann/Singleton. */
339 if (strcasecmp(last, ulast) && strncasecmp(last, ulast, strlen(last)) &&
340 strncasecmp(last, ulast, strlen(ulast)))
342 if (strlen(last) > 3 && strlen(ulast) < 3)
344 if (strcasecmp(first, ufirst) &&
345 strncasecmp(first, ufirst, strlen(first)) &&
346 strncasecmp(first, ufirst, strlen(ufirst)))
348 if (strlen(first) > 3 && strlen(ufirst) < 3)
350 /* Ignore the middle name since Moira doesn't have those reliably */
353 sqlstatus = sqlca.sqlcode;
354 EXEC SQL CLOSE csr_id;
358 reply(rc, NOT_FOUND_IN_DATABASE, "GETN", "d", NULL);
366 case US_ENROLL_NOT_ALLOWED:
367 reply(rc, ALREADY_REGISTERED, "INIT", "c", NULL, login);
371 reply(rc, ACCOUNT_DELETED, "INIT", "c", NULL, login);
375 reply(rc, NOT_ELIGIBLE, "INIT", "c", NULL);
383 sprintf(fullname, "%s %s%s%s", first, middle, *middle ? " " : "", last);
384 if (!strcmp(class, "MITS"))
385 strcpy(class, "STAFF");
391 com_err(whoami, errno, "in RIFO");
392 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
398 rc->reserved_username = 1;
399 rc->username = strdup(login);
402 com_err(whoami, errno, "in RIFO");
403 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
409 rc->suggestions = find_usernames(first, middle, last);
410 if (!rc->suggestions && errno)
412 com_err(whoami, errno, "in RIFO");
413 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(errno));
419 reply(rc, FOUND, "GETW", "c", NULL, fullname, class);
420 else if (!rc->username)
421 reply(rc, FOUND, "GETL", "c", rc->suggestions, fullname, class);
424 if (status == US_NO_LOGIN_YET)
426 status = check_kerberos(login);
427 if (status == MR_SUCCESS)
428 if (!strcmp(class, "LINCOLN"))
430 EXEC SQL SELECT string_id INTO :string_id FROM strings
431 WHERE string = 'LINCOLN: no pobox or filesys';
432 EXEC SQL UPDATE users SET comments = :string_id
433 WHERE login = :login;
436 status = register_user(rc->uid, login);
437 if (status == MR_IN_USE)
439 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
443 else if (status == MR_DOWN)
445 reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
448 else if (status != MR_SUCCESS)
450 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL,
451 error_message(status));
455 reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
459 void SWRD(reg_client *rc, int argc, char **argv)
464 if (!rc->id || argc != 6)
466 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
470 getwordlist(rc->id, words);
471 for (i = 0; i < 6; i++)
473 if (strcasecmp(strtrim(argv[i]), words[i]))
478 reply(rc, BAD_SIX_WORDS, "GETW", "d", NULL);
485 reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
487 reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
490 void LOGN(reg_client *rc, int argc, char **argv)
496 if (!rc->uid || rc->id || rc->username || argc != 1)
498 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
504 /* make sure someone's not trying to overrun reply */
505 if (strlen(login) > 100)
507 com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
512 if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
513 (login[0] == '_') || isdigit(login[0]))
515 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
516 3, USERS_LOGIN_SIZE - 1);
520 for (i = 0; i < strlen(login); i++)
522 if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
524 reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
525 3, USERS_LOGIN_SIZE - 1);
530 status = check_kerberos(login);
531 if (status == MR_SUCCESS)
532 status = register_user(rc->uid, login);
534 if (status == MR_IN_USE)
536 if (rc->reserved_username)
538 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
542 reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
545 else if (status == MR_DOWN)
547 reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
550 else if (status != MR_SUCCESS)
552 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
556 rc->username = strdup(login);
559 com_err(whoami, errno, "in LOGN");
560 reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, "Out of memory");
563 reply(rc, USERNAME_OK, "GETP", "c", NULL, login);
567 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^@ - ^O */
568 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ^P - ^_ */
569 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* SPACE - / */
570 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, /* 0 - ? */
571 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* : - O */
572 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, /* P - _ */
573 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* ` - o */
574 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, /* p - ^? */
575 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
576 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
577 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
578 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
579 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
580 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
581 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
582 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
585 void PSWD(reg_client *rc, int argc, char **argv)
588 char *password = argv[0], *p;
589 EXEC SQL BEGIN DECLARE SECTION;
590 char *login = rc->username;
591 EXEC SQL END DECLARE SECTION;
593 if (!rc->username || rc->id || argc != 1)
595 reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
599 /* password quality checking */
600 if (strlen(password) < 4)
602 reply(rc, PASSWORD_SHORT, "GETP", "c", NULL);
606 if (strlen(password) < 7)
608 for (p = password + 1; *p; p++)
610 if (ctypes[*p] != ctypes[*(p - 1)])
615 reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
620 if (!strcasecmp(password, "GykoR-66") ||
621 !strcasecmp(password, "slaRooBey") ||
622 !strcasecmp(password, "krang-its") ||
623 !strcasecmp(password, "2HotPeetzas") ||
624 !strcasecmp(password, "ItzAGurl"))
626 reply(rc, PASSWORD_SAMPLE, "GETP", "c", NULL);
630 status = register_kerberos(rc->username, password);
631 if (status == MR_QUALITY)
633 reply(rc, PASSWORD_SIMPLE, "GETP", "c", NULL);
636 else if (status == MR_IN_USE)
638 reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
644 com_err(whoami, status, "registering username with Kerberos");
645 reply(rc, KADM_ERROR, "INIT", "c", NULL, error_message(status));
648 EXEC SQL UPDATE users SET status = 1 WHERE login = :login;
651 reply(rc, DONE, "INIT", "c", NULL, rc->username);
654 void QUIT(reg_client *rc, int argc, char **argv)
658 /* Register a user in Moira */
659 int register_user(int uid, char *username)
661 char uidbuf[10], *qargv[3], *motd = NULL;
664 status = mr_connect(hostname);
668 status = mr_motd(&motd);
675 status = krb_get_svc_in_tkt(REG_SVR_PRINCIPAL, REG_SVR_INSTANCE,
676 krb_realmofhost(hostname), MOIRA_SNAME,
677 shorthostname, 1, KEYFILE);
679 status += ERROR_TABLE_BASE_krb;
681 status = mr_auth("reg_svr");
684 com_err(whoami, status, "authenticating to moira");
689 sprintf(uidbuf, "%d", uid);
693 status = mr_query("register_user", 3, qargv, NULL, NULL);
699 /* Find some typical available usernames */
701 char *uname_patterns[] = {
703 "fmllllll", /* jmdoe... (last name truncated) */
704 "flllllll", /* jdoe.... ("") */
705 "llllllll", /* doe..... ("") */
712 int num_patterns = sizeof(uname_patterns) / sizeof(char *);
714 char *find_usernames(char *first, char *middle, char *last)
716 EXEC SQL BEGIN DECLARE SECTION;
717 char username[2 * USERS_LOGIN_SIZE];
719 EXEC SQL END DECLARE SECTION;
721 char *pp, *up, *fp, *mp, *lp, *unames = NULL;
727 for (pat = 0; pat < num_patterns; pat++)
733 for (pp = uname_patterns[pat]; *pp; pp++)
743 if (up - username + strlen(first) < USERS_LOGIN_SIZE)
744 up += sprintf(up, "%s", first);
762 if (up - username + strlen(last) < USERS_LOGIN_SIZE)
763 up += sprintf(up, "%s", last);
771 if (strlen(username) < 3 || strlen(username) >= USERS_LOGIN_SIZE)
774 EXEC SQL SELECT COUNT(login) INTO :count FROM users
775 WHERE login = :username;
783 EXEC SQL SELECT COUNT(name) INTO :count FROM list
784 WHERE name = :username;
793 EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
794 WHERE label = :username;
806 unames = realloc(unames, strlen(unames) + strlen(username) + 3);
809 strcat(unames, ", ");
810 strcat(unames, username);
814 unames = strdup(username);
824 /* unames will be NULL if we couldn't suggest a username. Clear
825 errno so the caller can distinguish this from an error case. */
830 void fixname(char *name)
834 for (s = d = name; *s; s++)
842 void *xmalloc(size_t bytes)
844 void *buf = malloc(bytes);
849 com_err(whoami, errno, "in xmalloc");
853 void *xrealloc(void *ptr, size_t bytes)
855 void *buf = realloc(ptr, bytes);
860 com_err(whoami, errno, "in xrealloc");
864 char *xstrdup(char *str)
866 char *buf = strdup(str);
871 com_err(whoami, errno, "in xstrdup");
875 void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar)
879 fputs(whoami, stderr);
881 fprintf(stderr, "[#%d]", cl->clientid);
885 fputs(error_message(code), stderr);
889 vfprintf(stderr, fmt, pvar);
893 void sigshut(int sig)