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