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