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