Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / crypto / kerberosIV / server / kerberos.c
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 $ */
9 /* $DragonFly: src/crypto/kerberosIV/server/Attic/kerberos.c,v 1.2 2003/06/17 04:24:36 dillon Exp $ */
10
11 #include "config.h"
12 #include "protos.h"
13
14 RCSID("$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
95 static des_key_schedule master_key_schedule;
96 static des_cblock master_key;
97
98 static struct timeval kerb_time;
99 static u_char master_key_version;
100 static char *lt;
101 static int more;
102
103 static int mflag;               /* Are we invoked manually? */
104 static char *log_file = KRBLOG; /* name of alt. log file */
105 static int nflag;               /* don't check max age */
106 static int rflag;               /* alternate realm specified */
107
108 /* fields within the received request packet */
109 static char *req_name_ptr;
110 static char *req_inst_ptr;
111 static char *req_realm_ptr;
112 static u_int32_t req_time_ws;
113
114 static char local_realm[REALM_SZ];
115
116 /* options */
117 static int max_age = -1;
118 static int pause_int = -1;
119
120 /*
121  * Print usage message and exit.
122  */
123 static void
124 usage(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
138 static void
139 kerb_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
153 static void
154 hang(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
172 static int
173 check_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
226 static void
227 unseal(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 */
234 static int
235 set_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
262 static int
263 kerberos(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
473 static void
474 kerberos_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
507 static void
508 setup_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
532 static void
533 check_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
553 struct descr{
554     int s;
555     KTEXT_ST buf;
556     int type;
557     int timeout;
558     struct sockaddr_in addr;
559 };
560
561 static void
562 mksocket(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
596 static void loop(struct descr *fds, int maxfd);
597
598 struct port_spec {
599     int port;
600     int type;
601 };
602
603 static int
604 add_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
617 static void
618 make_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
710 int
711 main(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
895 static void
896 read_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
987 static fd_set readfds;
988
989 static void
990 loop(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 }