Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / crypto / kerberosIV / server / kerberos.c
CommitLineData
984263bc
MD
1/*
2 * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute
3 * of Technology.
4 *
5 * For copying and distribution information, please see the file
6 * <mit-copyright.h>.
7 */
8/* $FreeBSD: src/crypto/kerberosIV/server/kerberos.c,v 1.2.2.3 2003/02/14 22:37:37 nectar Exp $ */
1de703da 9/* $DragonFly: src/crypto/kerberosIV/server/Attic/kerberos.c,v 1.2 2003/06/17 04:24:36 dillon Exp $ */
984263bc
MD
10
11#include "config.h"
12#include "protos.h"
13
14RCSID("$Id: kerberos.c,v 1.87.2.3 2000/10/18 20:24:13 assar Exp $");
15
16/*
17 * If support for really large numbers of network interfaces is
18 * desired, define FD_SETSIZE to some suitable value.
19 */
20#define FD_SETSIZE (4*1024)
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <ctype.h>
26
27#ifdef HAVE_SYS_TYPES_H
28#include <sys/types.h>
29#endif
30
31#ifdef TIME_WITH_SYS_TIME
32#include <sys/time.h>
33#include <time.h>
34#elif defined(HAVE_SYS_TIME_H)
35#include <sys/time.h>
36#else
37#include <time.h>
38#endif
39
40#ifdef HAVE_SYS_SELECT_H
41#include <sys/select.h>
42#endif
43
44#include <errno.h>
45#ifdef HAVE_UNISTD_H
46#include <unistd.h>
47#endif
48
49#ifdef HAVE_SYS_SOCKET_H
50#include <sys/socket.h>
51#endif
52#ifdef HAVE_NETINET_IN_H
53#include <netinet/in.h>
54#endif
55#ifdef HAVE_ARPA_INET_H
56#include <arpa/inet.h>
57#endif
58
59#ifdef HAVE_SYS_STAT_H
60#include <sys/stat.h>
61#endif
62#ifdef HAVE_FCNTL_H
63#include <fcntl.h>
64#endif
65#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
66#include <sys/ioctl.h>
67#endif
68#ifdef HAVE_SYS_FILIO_H
69#include <sys/filio.h>
70#endif /* HAVE_SYS_FILIO_H */
71
72#ifdef HAVE_NETDB_H
73#include <netdb.h>
74#endif
75#include <err.h>
76
77#ifdef SOCKS
78#include <socks.h>
79#endif
80
81#include <roken.h>
82#include <base64.h>
83
84#define OPENSSL_DES_LIBDES_COMPATIBILITY
85#include <openssl/des.h>
86#include <krb.h>
87#include <krb_db.h>
88#include <prot.h>
89#include <klog.h>
90
91#include <krb_log.h>
92
93#include <kdc.h>
94
95static des_key_schedule master_key_schedule;
96static des_cblock master_key;
97
98static struct timeval kerb_time;
99static u_char master_key_version;
100static char *lt;
101static int more;
102
103static int mflag; /* Are we invoked manually? */
104static char *log_file = KRBLOG; /* name of alt. log file */
105static int nflag; /* don't check max age */
106static int rflag; /* alternate realm specified */
107
108/* fields within the received request packet */
109static char *req_name_ptr;
110static char *req_inst_ptr;
111static char *req_realm_ptr;
112static u_int32_t req_time_ws;
113
114static char local_realm[REALM_SZ];
115
116/* options */
117static int max_age = -1;
118static int pause_int = -1;
119
120/*
121 * Print usage message and exit.
122 */
123static void
124usage(void)
125{
126 fprintf(stderr, "Usage: %s [-s] [-m] [-n] [-p pause_seconds]"
127 " [-a max_age] [-l log_file] [-i address_to_listen_on]"
128 " [-r realm] [database_pathname]\n",
129 __progname);
130 exit(1);
131}
132
133/*
134 * kerb_err_reply creates an error reply packet and sends it to the
135 * client.
136 */
137
138static void
139kerb_err_reply(int f, struct sockaddr_in *client, int err, char *string)
140{
141 static KTEXT_ST e_pkt_st;
142 KTEXT e_pkt = &e_pkt_st;
143 static char e_msg[128];
144
145 snprintf (e_msg, sizeof(e_msg),
146 "\nKerberos error -- %s", string);
147 cr_err_reply(e_pkt, req_name_ptr, req_inst_ptr, req_realm_ptr,
148 req_time_ws, err, e_msg);
149 sendto(f, (char*)e_pkt->dat, e_pkt->length, 0, (struct sockaddr *)client,
150 sizeof(*client));
151}
152
153static void
154hang(void)
155{
156 if (pause_int == -1) {
157 klog(L_KRB_PERR, "Kerberos will pause so as not to loop init");
158 for (;;)
159 pause();
160 } else {
161 char buf[256];
162 snprintf(buf, sizeof(buf),
163 "Kerberos will wait %d seconds before dying so as not to loop init",
164 pause_int);
165 klog(L_KRB_PERR, buf);
166 sleep(pause_int);
167 klog(L_KRB_PERR, "Do svedania....\n");
168 exit(1);
169 }
170}
171
172static int
173check_princ(char *p_name, char *instance, unsigned int lifetime, Principal *p)
174{
175 static int n;
176 static int more;
177
178 n = kerb_get_principal(p_name, instance, p, 1, &more);
179
180 if (n < 0) {
181 lt = klog(L_KRB_PERR, "Database unavailable!");
182 hang();
183 }
184
185 /*
186 * if more than one p_name, pick one, randomly create a session key,
187 * compute maximum lifetime, lookup authorizations if applicable,
188 * and stuff into cipher.
189 */
190 if (n == 0) {
191 /* service unknown, log error, skip to next request */
192 lt = klog(L_ERR_UNK, "UNKNOWN %s.%s", p_name, instance);
193 return KERB_ERR_PRINCIPAL_UNKNOWN;
194 }
195 if (more) {
196 /* not unique, log error */
197 lt = klog(L_ERR_NUN, "Principal not unique %s.%s", p_name, instance);
198 return KERB_ERR_PRINCIPAL_NOT_UNIQUE;
199 }
200 /* If the user's key is null, we want to return an error */
201 if ((p->key_low == 0) && (p->key_high == 0)) {
202 /* User has a null key */
203 lt = klog(L_ERR_NKY, "Null key %s.%s", p_name, instance);
204 return KERB_ERR_NULL_KEY;
205 }
206 if (master_key_version != p->kdc_key_ver) {
207 /* log error reply */
208 lt = klog(L_ERR_MKV,
209 "Incorrect master key version for %s.%s: %d (should be %d)",
210 p->name, p->instance, p->kdc_key_ver, master_key_version);
211 return KERB_ERR_NAME_MAST_KEY_VER;
212 }
213 /* make sure the service hasn't expired */
214 if ((u_int32_t) p->exp_date < (u_int32_t) kerb_time.tv_sec) {
215 /* service did expire, log it */
216 time_t t = p->exp_date;
217 lt = klog(L_ERR_SEXP,
218 "Principal %s.%s expired at %s", p->name, p->instance,
219 krb_stime(&t));
220 return KERB_ERR_NAME_EXP;
221 }
222 /* ok is zero */
223 return 0;
224}
225
226static void
227unseal(des_cblock *key)
228{
229 kdb_encrypt_key(key, key, &master_key, master_key_schedule, DES_DECRYPT);
230}
231
232
233/* Set the key for krb_rd_req so we can check tgt */
234static int
235set_tgtkey(char *r)
236 /* Realm for desired key */
237{
238 int n;
239 static char lastrealm[REALM_SZ];
240 Principal p_st;
241 Principal *p = &p_st;
242 des_cblock key;
243
244 if (!strcmp(lastrealm, r))
245 return (KSUCCESS);
246
247 klog(L_ALL_REQ, "Getting key for %s", r);
248
249 n = kerb_get_principal(KRB_TICKET_GRANTING_TICKET, r, p, 1, &more);
250 if (n == 0)
251 return (KFAILURE);
252
253 /* unseal tgt key from master key */
254 copy_to_key(&p->key_low, &p->key_high, key);
255 unseal(&key);
256 krb_set_key(key, 0);
257 strlcpy (lastrealm, r, REALM_SZ);
258 return (KSUCCESS);
259}
260
261
262static int
263kerberos(unsigned char *buf, int len,
264 char *proto, struct sockaddr_in *client,
265 struct sockaddr_in *server,
266 KTEXT rpkt)
267{
268 int pvno;
269 int msg_type;
270 int lsb;
271 int life;
272 int flags = 0;
273 char name[ANAME_SZ], inst[INST_SZ], realm[REALM_SZ];
274 char service[SNAME_SZ], sinst[INST_SZ];
275 u_int32_t req_time;
276 static KTEXT_ST ticket, cipher, adat;
277 KTEXT tk = &ticket, ciph = &cipher, auth = &adat;
278 AUTH_DAT ad;
279 des_cblock session, key;
280 int err;
281 Principal a_name, s_name;
282
283 char *msg;
284
285
286 unsigned char *p = buf;
287 if(len < 2){
288 strlcpy((char*)rpkt->dat,
289 "Packet too short",
290 sizeof(rpkt->dat));
291 return KFAILURE;
292 }
293
294 gettimeofday(&kerb_time, NULL);
295
296 pvno = *p++;
297 if(pvno != KRB_PROT_VERSION){
298 msg = klog(L_KRB_PERR, "KRB protocol version mismatch (%d)", pvno);
299 strlcpy((char*)rpkt->dat,
300 msg,
301 sizeof(rpkt->dat));
302 return KERB_ERR_PKT_VER;
303 }
304 msg_type = *p++;
305 lsb = msg_type & 1;
306 msg_type &= ~1;
307 switch(msg_type){
308 case AUTH_MSG_KDC_REQUEST:
309 /* XXX range check */
310 p += krb_get_nir(p, name, sizeof(name),
311 inst, sizeof(inst),
312 realm, sizeof(realm));
313 p += krb_get_int(p, &req_time, 4, lsb);
314 life = *p++;
315 p += krb_get_nir(p, service, sizeof(service),
316 sinst, sizeof(sinst), NULL, 0);
317 klog(L_INI_REQ,
318 "AS REQ %s.%s@%s for %s.%s from %s (%s/%u)",
319 name, inst, realm, service, sinst,
320 inet_ntoa(client->sin_addr),
321 proto, ntohs(server->sin_port));
322 if((err = check_princ(name, inst, 0, &a_name))){
323 strlcpy((char*)rpkt->dat,
324 krb_get_err_text(err),
325 sizeof(rpkt->dat));
326 return err;
327 }
328 tk->length = 0;
329 if((err = check_princ(service, sinst, 0, &s_name))){
330 strlcpy((char*)rpkt->dat,
331 krb_get_err_text(err),
332 sizeof(rpkt->dat));
333 return err;
334 }
335 life = min(life, s_name.max_life);
336 life = min(life, a_name.max_life);
337
338 des_random_key(session);
339 copy_to_key(&s_name.key_low, &s_name.key_high, key);
340 unseal(&key);
341 krb_create_ticket(tk, flags, a_name.name, a_name.instance,
342 local_realm, client->sin_addr.s_addr,
343 session,
344 life, kerb_time.tv_sec,
345 s_name.name, s_name.instance, &key);
346 copy_to_key(&a_name.key_low, &a_name.key_high, key);
347 unseal(&key);
348 create_ciph(ciph, session, s_name.name, s_name.instance,
349 local_realm, life, s_name.key_version, tk,
350 kerb_time.tv_sec, &key);
351 memset(&session, 0, sizeof(session));
352 memset(&key, 0, sizeof(key));
353 {
354 KTEXT r;
355 r = create_auth_reply(name, inst, realm, req_time, 0,
356 a_name.exp_date, a_name.key_version, ciph);
357 memcpy(rpkt, r, sizeof(*rpkt));
358 }
359 return 0;
360 case AUTH_MSG_APPL_REQUEST:
361 strlcpy(realm, (char*)buf + 3, REALM_SZ);
362 if((err = set_tgtkey(realm))){
363 msg = klog(L_ERR_UNK,
364 "Unknown realm %s from %s (%s/%u)",
365 realm, inet_ntoa(client->sin_addr),
366 proto, ntohs(server->sin_port));
367 strlcpy((char*)rpkt->dat,
368 msg,
369 sizeof(rpkt->dat));
370 return err;
371 }
372 p = buf + strlen(realm) + 4;
373 p = p + p[0] + p[1] + 2;
374 auth->length = p - buf;
375 memcpy(auth->dat, buf, auth->length);
376 err = krb_rd_req(auth, KRB_TICKET_GRANTING_TICKET,
377 realm, client->sin_addr.s_addr, &ad, 0);
378 if(err){
379 msg = klog(L_ERR_UNK,
380 "krb_rd_req from %s (%s/%u): %s",
381 inet_ntoa(client->sin_addr),
382 proto,
383 ntohs(server->sin_port),
384 krb_get_err_text(err));
385 strlcpy((char*)rpkt->dat,
386 msg,
387 sizeof(rpkt->dat));
388 return err;
389 }
390 p += krb_get_int(p, &req_time, 4, lsb);
391 life = *p++;
392 p += krb_get_nir(p, service, sizeof(service),
393 sinst, sizeof(sinst), NULL, 0);
394 klog(L_APPL_REQ,
395 "APPL REQ %s.%s@%s for %s.%s from %s (%s/%u)",
396 ad.pname, ad.pinst, ad.prealm,
397 service, sinst,
398 inet_ntoa(client->sin_addr),
399 proto,
400 ntohs(server->sin_port));
401
402 if(strcmp(ad.prealm, realm)){
403 msg = klog(L_ERR_UNK, "Can't hop realms: %s -> %s",
404 realm, ad.prealm);
405 strlcpy((char*)rpkt->dat,
406 msg,
407 sizeof(rpkt->dat));
408 return KERB_ERR_PRINCIPAL_UNKNOWN;
409 }
410
411 if(!strcmp(service, "changepw")){
412 strlcpy((char*)rpkt->dat,
413 "Can't authorize password changed based on TGT",
414 sizeof(rpkt->dat));
415 return KERB_ERR_PRINCIPAL_UNKNOWN;
416 }
417
418 err = check_princ(service, sinst, life, &s_name);
419 if(err){
420 strlcpy((char*)rpkt->dat,
421 krb_get_err_text(err),
422 sizeof(rpkt->dat));
423 return err;
424 }
425 life = min(life,
426 krb_time_to_life(kerb_time.tv_sec,
427 krb_life_to_time(ad.time_sec,
428 ad.life)));
429 life = min(life, s_name.max_life);
430 copy_to_key(&s_name.key_low, &s_name.key_high, key);
431 unseal(&key);
432 des_random_key(session);
433 krb_create_ticket(tk, flags, ad.pname, ad.pinst, ad.prealm,
434 client->sin_addr.s_addr, &session,
435 life, kerb_time.tv_sec,
436 s_name.name, s_name.instance,
437 &key);
438
439 memset(&key, 0, sizeof(key));
440
441 create_ciph(ciph, session, service, sinst, local_realm,
442 life, s_name.key_version, tk,
443 kerb_time.tv_sec, &ad.session);
444
445 memset(&session, 0, sizeof(session));
446 memset(ad.session, 0, sizeof(ad.session));
447 {
448 KTEXT r;
449 r =create_auth_reply(ad.pname, ad.pinst, ad.prealm,
450 req_time, 0, 0, 0, ciph);
451 memcpy(rpkt, r, sizeof(*rpkt));
452 }
453 memset(&s_name, 0, sizeof(s_name));
454 return 0;
455
456 case AUTH_MSG_ERR_REPLY:
457 return -1;
458 default:
459 msg = klog(L_KRB_PERR,
460 "Unknown message type: %d from %s (%s/%u)",
461 msg_type,
462 inet_ntoa(client->sin_addr),
463 proto,
464 ntohs(server->sin_port));
465 strlcpy((char*)rpkt->dat,
466 msg,
467 sizeof(rpkt->dat));
468 return KFAILURE;
469 }
470}
471
472
473static void
474kerberos_wrap(int s, KTEXT data, char *proto, struct sockaddr_in *client,
475 struct sockaddr_in *server)
476{
477 KTEXT_ST pkt;
478 int http_flag = strcmp(proto, "http") == 0;
479 int err = kerberos(data->dat, data->length, proto, client, server, &pkt);
480 if(err == -1)
481 return;
482 if(http_flag){
483 const char *msg =
484 "HTTP/1.1 200 OK\r\n"
485 "Server: KTH-KRB/1\r\n"
486 "Content-type: application/octet-stream\r\n"
487 "Content-transfer-encoding: binary\r\n\r\n";
488 sendto(s, msg, strlen(msg), 0, (struct sockaddr *)client,
489 sizeof(*client));
490 }
491 if(err){
492 kerb_err_reply(s, client, err, (char*)pkt.dat);
493 return;
494 }
495 sendto(s, pkt.dat, pkt.length, 0, (struct sockaddr *)client,
496 sizeof(*client));
497}
498
499
500/*
501 * setup_disc
502 *
503 * disconnect all descriptors, remove ourself from the process
504 * group that spawned us.
505 */
506
507static void
508setup_disc(void)
509{
510 int s;
511
512 for (s = 0; s < 3; s++) {
513 close(s);
514 }
515
516 open("/dev/null", 0);
517 dup2(0, 1);
518 dup2(0, 2);
519
520 setsid();
521
522 chdir("/tmp");
523 return;
524}
525
526/*
527 * Make sure that database isn't stale.
528 *
529 * Exit if it is; we don't want to tell lies.
530 */
531
532static void
533check_db_age(void)
534{
535 long age;
536
537 if (max_age != -1) {
538 /* Requires existance of kerb_get_db_age() */
539 gettimeofday(&kerb_time, 0);
540 age = kerb_get_db_age();
541 if (age == 0) {
542 klog(L_KRB_PERR, "Database currently being updated!");
543 hang();
544 }
545 if ((age + max_age) < kerb_time.tv_sec) {
546 klog(L_KRB_PERR, "Database out of date!");
547 hang();
548 /* NOTREACHED */
549 }
550 }
551}
552
553struct descr{
554 int s;
555 KTEXT_ST buf;
556 int type;
557 int timeout;
558 struct sockaddr_in addr;
559};
560
561static void
562mksocket(struct descr *d, struct in_addr addr, int type,
563 const char *service, int port)
564{
565 int on = 1;
566 int sock;
567
568 memset(d, 0, sizeof(struct descr));
569 if ((sock = socket(AF_INET, type, 0)) < 0)
570 err (1, "socket");
571 if (sock >= FD_SETSIZE) {
572 errno = EMFILE;
573 errx(1, "Aborting: too many descriptors");
574 }
575#if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT)
576 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
577 sizeof(on)) < 0)
578 warn ("setsockopt (SO_REUSEADDR)");
579#endif
580 memset(&d->addr, 0, sizeof(d->addr));
581 d->addr.sin_family = AF_INET;
582 d->addr.sin_port = port;
583 d->addr.sin_addr = addr;
584 if (bind(sock, (struct sockaddr *)&d->addr, sizeof(d->addr)) < 0)
585 err (1, "bind '%s/%s' (%d)",
586 service, (type == SOCK_DGRAM) ? "udp" : "tcp",
587 ntohs(d->addr.sin_port));
588
589 if(type == SOCK_STREAM)
590 listen(sock, SOMAXCONN);
591 d->s = sock;
592 d->type = type;
593}
594
595
596static void loop(struct descr *fds, int maxfd);
597
598struct port_spec {
599 int port;
600 int type;
601};
602
603static int
604add_port(struct port_spec **ports, int *num_ports, int port, int type)
605{
606 struct port_spec *tmp;
607 tmp = realloc(*ports, (*num_ports + 1) * sizeof(*tmp));
608 if(tmp == NULL)
609 return ENOMEM;
610 *ports = tmp;
611 tmp[*num_ports].port = port;
612 tmp[*num_ports].type = type;
613 (*num_ports)++;
614 return 0;
615}
616
617static void
618make_sockets(const char *port_spec, struct in_addr *i_addr,
619 struct descr **fds, int *nfds)
620{
621 int tp;
622 struct in_addr *a;
623 char *p, *q, *pos = NULL;
624 struct servent *sp;
625 struct port_spec *ports = NULL;
626 int num_ports = 0;
627 int i, j;
628 char *port_spec_copy = strdup (port_spec);
629
630 if (port_spec_copy == NULL)
631 err (1, "strdup");
632
633 for(p = strtok_r(port_spec_copy, ", \t", &pos);
634 p;
635 p = strtok_r(NULL, ", \t", &pos)){
636 if(strcmp(p, "+") == 0){
637 add_port(&ports, &num_ports, 88, SOCK_DGRAM);
638 add_port(&ports, &num_ports, 88, SOCK_STREAM);
639 add_port(&ports, &num_ports, 750, SOCK_DGRAM);
640 add_port(&ports, &num_ports, 750, SOCK_STREAM);
641 }else{
642 q = strchr(p, '/');
643 if(q){
644 *q = 0;
645 q++;
646 }
647 sp = getservbyname(p, q);
648 if(sp)
649 tp = ntohs(sp->s_port);
650 else if(sscanf(p, "%d", &tp) != 1) {
651 warnx("Unknown port: %s%s%s", p, q ? "/" : "", q ? q : "");
652 continue;
653 }
654 if(q){
655 if(strcasecmp(q, "tcp") == 0)
656 add_port(&ports, &num_ports, tp, SOCK_STREAM);
657 else if(strcasecmp(q, "udp") == 0)
658 add_port(&ports, &num_ports, tp, SOCK_DGRAM);
659 else
660 warnx("Unknown protocol type: %s", q);
661 }else{
662 add_port(&ports, &num_ports, tp, SOCK_DGRAM);
663 add_port(&ports, &num_ports, tp, SOCK_STREAM);
664 }
665 }
666 }
667 free (port_spec_copy);
668
669 if(num_ports == 0)
670 errx(1, "No valid ports specified!");
671
672 if (i_addr) {
673 *nfds = 1;
674 a = malloc(sizeof(*a) * *nfds);
675 if (a == NULL)
676 errx (1, "Failed to allocate %lu bytes",
677 (unsigned long)(sizeof(*a) * *nfds));
678 memcpy(a, i_addr, sizeof(struct in_addr));
679 } else
680 *nfds = k_get_all_addrs (&a);
681 if (*nfds < 0) {
682 struct in_addr any;
683
684 any.s_addr = INADDR_ANY;
685
686 warnx ("Could not get local addresses, binding to INADDR_ANY");
687 *nfds = 1;
688 a = malloc(sizeof(*a) * *nfds);
689 if (a == NULL)
690 errx (1, "Failed to allocate %lu bytes",
691 (unsigned long)(sizeof(*a) * *nfds));
692 memcpy(a, &any, sizeof(struct in_addr));
693 }
694 *fds = malloc(*nfds * num_ports * sizeof(**fds));
695 if (*fds == NULL)
696 errx (1, "Failed to allocate %lu bytes",
697 (unsigned long)(*nfds * num_ports * sizeof(**fds)));
698 for (i = 0; i < *nfds; i++) {
699 for(j = 0; j < num_ports; j++) {
700 mksocket(*fds + num_ports * i + j, a[i],
701 ports[j].type, "", htons(ports[j].port));
702 }
703 }
704 *nfds *= num_ports;
705 free(ports);
706 free (a);
707}
708
709
710int
711main(int argc, char **argv)
712{
713 int child;
714 int c;
715 struct descr *fds;
716 int nfds;
717 int n;
718 int kerror;
719 int i_flag = 0;
720 struct in_addr i_addr;
721 char *port_spec = "+";
722
723 umask(077); /* Create protected files */
724
725 set_progname (argv[0]);
726
727 while ((c = getopt(argc, argv, "snmp:P:a:l:r:i:")) != -1) {
728 switch(c) {
729 case 's':
730 /*
731 * Set parameters to slave server defaults.
732 */
733 if (max_age == -1 && !nflag)
734 max_age = THREE_DAYS; /* Survive weekend */
735 if (pause_int == -1)
736 pause_int = FIVE_MINUTES; /* 5 minutes */
737 break;
738 case 'n':
739 max_age = -1; /* don't check max age. */
740 nflag++;
741 break;
742 case 'm':
743 mflag++; /* running manually; prompt for master key */
744 break;
745 case 'p': {
746 /* Set pause interval. */
747 char *tmp;
748
749 pause_int = strtol (optarg, &tmp, 0);
750 if (pause_int == 0 && tmp == optarg) {
751 fprintf(stderr, "pause_int `%s' not a number\n", optarg);
752 usage ();
753 }
754
755 if ((pause_int < 5) || (pause_int > ONE_HOUR)) {
756 fprintf(stderr, "pause_int must be between 5 and 3600 seconds.\n");
757 usage();
758 }
759 break;
760 }
761 case 'P':
762 port_spec = optarg;
763 break;
764 case 'a': {
765 /* Set max age. */
766 char *tmp;
767
768 max_age = strtol (optarg, &tmp, 0);
769 if (max_age == 0 && tmp == optarg) {
770 fprintf (stderr, "max_age `%s' not a number\n", optarg);
771 usage ();
772 }
773 if ((max_age < ONE_HOUR) || (max_age > THREE_DAYS)) {
774 fprintf(stderr, "max_age must be between one hour and "
775 "three days, in seconds\n");
776 usage();
777 }
778 break;
779 }
780 case 'l':
781 /* Set alternate log file */
782 log_file = optarg;
783 break;
784 case 'r':
785 /* Set realm name */
786 rflag++;
787 strlcpy(local_realm, optarg, sizeof(local_realm));
788 break;
789 case 'i':
790 /* Only listen on this address */
791 if(inet_aton (optarg, &i_addr) == 0) {
792 fprintf (stderr, "Bad address: %s\n", optarg);
793 exit (1);
794 }
795 ++i_flag;
796 break;
797 default:
798 usage();
799 break;
800 }
801 }
802
803 if (optind == (argc-1)) {
804 if (kerb_db_set_name(argv[optind]) != 0) {
805 fprintf(stderr, "Could not set alternate database name\n");
806 exit(1);
807 }
808 optind++;
809 }
810
811 if (optind != argc)
812 usage();
813
814 printf("Kerberos server starting\n");
815
816 if ((!nflag) && (max_age != -1))
817 printf("\tMaximum database age: %d seconds\n", max_age);
818 if (pause_int != -1)
819 printf("\tSleep for %d seconds on error\n", pause_int);
820 else
821 printf("\tSleep forever on error\n");
822 if (mflag)
823 printf("\tMaster key will be entered manually\n");
824
825 printf("\tLog file is %s\n", log_file);
826
827 kset_logfile(log_file);
828
829 make_sockets(port_spec, i_flag ? &i_addr : NULL, &fds, &nfds);
830
831 /* do all the database and cache inits */
832 if ((n = kerb_init())) {
833 if (mflag) {
834 printf("Kerberos db and cache init ");
835 printf("failed = %d ...exiting\n", n);
836 exit (1);
837 } else {
838 klog(L_KRB_PERR,
839 "Kerberos db and cache init failed = %d ...exiting", n);
840 hang();
841 }
842 }
843
844 /* Make sure database isn't stale */
845 check_db_age();
846
847 /* setup master key */
848 if (kdb_get_master_key (mflag, &master_key, master_key_schedule) != 0) {
849 klog (L_KRB_PERR, "kerberos: couldn't get master key.");
850 exit (1);
851 }
852 kerror = kdb_verify_master_key (&master_key, master_key_schedule, stdout);
853 if (kerror < 0) {
854 klog (L_KRB_PERR, "Can't verify master key.");
855 memset(master_key, 0, sizeof (master_key));
856 memset (master_key_schedule, 0, sizeof (master_key_schedule));
857 exit (1);
858 }
859
860 master_key_version = (u_char) kerror;
861
862 fprintf(stdout, "\nCurrent Kerberos master key version is %d\n",
863 master_key_version);
864
865 if (!rflag) {
866 /* Look up our local realm */
867 krb_get_lrealm(local_realm, 1);
868 }
869 fprintf(stdout, "Local realm: %s\n", local_realm);
870 fflush(stdout);
871
872 if (set_tgtkey(local_realm)) {
873 /* Ticket granting service unknown */
874 klog(L_KRB_PERR, "Ticket granting ticket service unknown");
875 fprintf(stderr, "Ticket granting ticket service unknown\n");
876 exit(1);
877 }
878 if (mflag) {
879 if ((child = fork()) != 0) {
880 printf("Kerberos started, PID=%d\n", child);
881 exit(0);
882 }
883 setup_disc();
884 }
885
886 klog(L_ALL_REQ, "Starting Kerberos for %s (kvno %d)",
887 local_realm, master_key_version);
888
889 /* receive loop */
890 loop(fds, nfds);
891 exit(1);
892}
893
894
895static void
896read_socket(struct descr *n)
897{
898 int b;
899 struct sockaddr_in from;
900 int fromlen = sizeof(from);
901 b = recvfrom(n->s, n->buf.dat + n->buf.length,
902 MAX_PKT_LEN - n->buf.length, 0,
903 (struct sockaddr *)&from, &fromlen);
904 if(b < 0){
905 if(n->type == SOCK_STREAM){
906 close(n->s);
907 n->s = -1;
908 }
909 n->buf.length = 0;
910 return;
911 }
912 n->buf.length += b;
913 if(n->type == SOCK_STREAM){
914 char *proto = "tcp";
915 if(n->buf.length > 4 &&
916 strncmp((char *)n->buf.dat, "GET ", 4) == 0 &&
917 strncmp((char *)n->buf.dat + n->buf.length - 4,
918 "\r\n\r\n", 4) == 0){
919 char *p;
920 char *save = NULL;
921
922 n->buf.dat[n->buf.length - 1] = 0;
923 strtok_r((char *)n->buf.dat, " \t\r\n", &save);
924 p = strtok_r(NULL, " \t\r\n", &save);
925 if(p == NULL)
926 p = "";
927 if(*p == '/') p++;
928 n->buf.length = base64_decode(p, n->buf.dat);
929 if(n->buf.length <= 0){
930 const char *msg =
931 "HTTP/1.1 404 Not found\r\n"
932 "Server: KTH-KRB/1\r\n"
933 "Content-type: text/html\r\n"
934 "Content-transfer-encoding: 8bit\r\n\r\n"
935 "<TITLE>404 Not found</TITLE>\r\n"
936 "<H1>404 Not found</H1>\r\n"
937 "That page does not exist. Information about "
938 "<A HREF=\"http://www.pdc.kth.se/kth-krb\">KTH-KRB</A> "
939 "is available elsewhere.\r\n";
940 fromlen = sizeof(from);
941 if(getpeername(n->s,(struct sockaddr*)&from, &fromlen) == 0)
942 klog(L_KRB_PERR, "Unknown HTTP request from %s",
943 inet_ntoa(from.sin_addr));
944 else
945 klog(L_KRB_PERR, "Unknown HTTP request from <unknown>");
946 write(n->s, msg, strlen(msg));
947 close(n->s);
948 n->s = -1;
949 n->buf.length = 0;
950 return;
951 }
952 proto = "http";
953 b = 0;
954 }
955 else if(n->buf.length >= 4 && n->buf.dat[0] == 0){
956 /* if this is a new type of packet (with
957 the length attached to the head of the
958 packet), and there is no more data to
959 be read, fake an old packet, so the
960 code below will work */
961 u_int32_t len;
962 krb_get_int(n->buf.dat, &len, 4, 0);
963 if(n->buf.length == len + 4){
964 memmove(n->buf.dat, n->buf.dat + 4, len);
965 b = 0;
966 }
967 }
968 if(b == 0){
969 /* handle request if there are
970 no more bytes to read */
971 fromlen = sizeof(from);
972 getpeername(n->s,(struct sockaddr*)&from, &fromlen);
973 kerberos_wrap(n->s, &n->buf, proto, &from,
974 &n->addr);
975 n->buf.length = 0;
976 close(n->s);
977 n->s = -1;
978 }
979 }else{
980 /* udp packets are atomic */
981 kerberos_wrap(n->s, &n->buf, "udp", &from,
982 &n->addr);
983 n->buf.length = 0;
984 }
985}
986
987static fd_set readfds;
988
989static void
990loop(struct descr *fds, int base_nfds)
991{
992 int nfds = base_nfds;
993 int max_tcp = min(FD_SETSIZE, getdtablesize()) - fds[base_nfds - 1].s;
994 if (max_tcp <= 10) {
995 errno = EMFILE;
996 errx(1, "Aborting: too many descriptors");
997 }
998 max_tcp -= 10; /* We need a few extra for DB, logs, etc. */
999 if (max_tcp > 100) max_tcp = 100; /* Keep to some sane limit. */
1000
1001 for (;;) {
1002 int ret;
1003 struct timeval tv;
1004 int next_timeout = 10; /* In seconds */
1005 int maxfd = 0;
1006 struct descr *n, *minfree;
1007 int accepted; /* accept at most one socket per `round' */
1008
1009 FD_ZERO(&readfds);
1010 gettimeofday(&tv, NULL);
1011 maxfd = 0;
1012 minfree = NULL;
1013 /* Remove expired TCP sockets, and add all other
1014 to the set we are selecting on */
1015 for(n = fds; n < fds + nfds; n++){
1016 if(n->s >= 0 && n->timeout && tv.tv_sec > n->timeout){
1017 kerb_err_reply(n->s, NULL, KERB_ERR_TIMEOUT, "Timeout");
1018 close(n->s);
1019 n->s = -1;
1020 }
1021 if(n->s < 0){
1022 if(minfree == NULL) minfree = n;
1023 continue;
1024 }
1025 FD_SET(n->s, &readfds);
1026 maxfd = max(maxfd, n->s);
1027 next_timeout = min(next_timeout, tv.tv_sec - n->timeout);
1028 }
1029 /* add more space for sockets */
1030 if (minfree == NULL && nfds < base_nfds + max_tcp) {
1031 int i = nfds;
1032 struct descr *new;
1033 nfds *=2;
1034 if (nfds > base_nfds + max_tcp)
1035 nfds = base_nfds + max_tcp;
1036 new = realloc(fds, sizeof(struct descr) * nfds);
1037 if(new){
1038 fds = new;
1039 minfree = fds + i;
1040 for(; i < nfds; i++) fds[i].s = -1;
1041 }
1042 }
1043 if (minfree == NULL) {
1044 /*
1045 * We are possibly the subject of a DOS attack, pick a TCP
1046 * connection at random and drop it.
1047 */
1048 int r = rand() % (nfds - base_nfds);
1049 r = r + base_nfds;
1050 FD_CLR(fds[r].s, &readfds);
1051 close(fds[r].s);
1052 fds[r].s = -1;
1053 minfree = &fds[r];
1054 }
1055 if (next_timeout < 0) next_timeout = 0;
1056 tv.tv_sec = next_timeout;
1057 tv.tv_usec = 0;
1058 ret = select(maxfd + 1, &readfds, 0, 0, &tv);
1059 if (ret < 0) {
1060 if (errno != EINTR)
1061 klog(L_KRB_PERR, "select: %s", strerror(errno));
1062 continue;
1063 }
1064 accepted = 0;
1065 for (n = fds; n < fds + nfds; n++){
1066 if(n->s < 0) continue;
1067 if (FD_ISSET(n->s, &readfds)){
1068 if(n->type == SOCK_STREAM && n->timeout == 0){
1069 /* add accepted socket to list of sockets we are
1070 selecting on */
1071 int s;
1072 if(accepted) continue;
1073 accepted = 1;
1074 s = accept(n->s, NULL, 0);
1075 if (minfree == NULL || s >= FD_SETSIZE) {
1076 close(s);
1077 }else{
1078 minfree->s = s;
1079 minfree->type = SOCK_STREAM;
1080 gettimeofday(&tv, NULL);
1081 minfree->timeout = tv.tv_sec + 4; /* XXX */
1082 minfree->buf.length = 0;
1083 memcpy(&minfree->addr, &n->addr, sizeof(minfree->addr));
1084 }
1085 }else
1086 read_socket(n);
1087 }
1088 }
1089 }
1090}