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