]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/bdump.c
168c7c0c31a479289ed9e18b22bae64e62913b0d
[1ts-debian.git] / zephyr / server / bdump.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for dumping server state between servers.
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/bdump.c,v $
7  *      $Id$
8  *      $Author$
9  *
10  *      Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
11  *      For copying and distribution information, see the file
12  *      "mit-copyright.h". 
13  */
14
15 #include <zephyr/mit-copyright.h>
16 #include "zserver.h"
17 #include <sys/socket.h>
18 #include <com_err.h>
19
20 #ifndef lint
21 static const char rcsid_bdump_c[] = "$Id$";
22 #endif /* lint */
23
24 /*
25  * External functions are:
26  *
27  * void bdump_offer(who)
28  *      strut sockaddr_in *who;
29  *
30  * void bdump_send()
31  *
32  * void bdump_get(notice, auth, who, server)
33  *      ZNotice_t *notice;
34  *      int auth;
35  *      struct sockaddr_in *who;
36  *      Server *server;
37  *
38  * Code_t bdump_send_list_tcp(kind, port, class, inst, opcode,
39  *                          sender, recip, lyst, num)
40  *      ZNotice_Kind_t kind;
41  *      u_short port;
42  *      char *class, *inst, *opcode, *sender, *recip;
43  *      char *lyst[];
44  *      int num;
45  */
46
47 #if defined(HAVE_KRB5) && 0
48 int krb5_init_keyblock(krb5_context context,
49         krb5_enctype type,
50         size_t size,
51         krb5_keyblock **akey)
52 {
53 krb5_error_code ret;
54 size_t len;
55 krb5_keyblock *key;
56
57 *akey=NULL;
58 key=malloc(sizeof(*key));
59 memset(key, 0, sizeof(*key));
60 ret = krb5_enctype_keysize(context, type, &len);
61 if (ret)
62 return ret;
63
64 if (len != size) {
65 krb5_set_error_string(context, "Encryption key %d is %lu bytes "
66 "long, %lu was passed in",
67 type, (unsigned long)len, (unsigned long)size);
68 return KRB5_PROG_ETYPE_NOSUPP;
69 }
70
71 ret = krb5_data_alloc(&key->keyvalue, len);
72 if(ret) {
73 krb5_set_error_string(context, "malloc failed: %lu",
74 (unsigned long)len);
75 return ret;
76 }
77 key->keytype = type;
78 *akey=key;
79 return 0;
80 }
81 #endif
82
83
84 static void close_bdump(void* arg);
85 static Code_t bdump_send_loop(Server *server);
86 static Code_t bdump_recv_loop(Server *server);
87 static void bdump_get_v12(ZNotice_t *, int, struct sockaddr_in *,
88                                Server *);
89 static Code_t get_packet(void *packet, int len, int *retlen);
90 static Code_t extract_sin(ZNotice_t *notice, struct sockaddr_in *target);
91 static Code_t send_done(void);
92 static Code_t send_list(ZNotice_Kind_t kind, int port, char *class_name,
93                              char *inst, char *opcode, char *sender,
94                              char *recip, char **lyst, int num);
95 static Code_t send_normal_tcp(ZNotice_Kind_t kind, int port,
96                                    char *class_name,
97                                    char *inst, char *opcode, char *sender,
98                                    char *recip, char *message, int len);
99 static int net_read(FILE *f, char *buf, int len);
100 static int net_write(FILE *f, char *buf, int len);
101 static int setup_file_pointers(void);
102 static void shutdown_file_pointers(void);
103 static void cleanup(Server *server);
104
105 #ifdef HAVE_KRB5
106 static int des_service_decrypt(unsigned char *in, unsigned char *out);
107 #endif
108 #ifdef HAVE_KRB5
109 static long ticket5_time;
110 #define TKT5LIFETIME 8*60*60
111 #define tkt5_lifetime(val) (val)
112 #endif
113
114 #ifdef HAVE_KRB4
115 static long ticket_time;
116
117 #define TKTLIFETIME     120
118 #define tkt_lifetime(val) ((long) val * 5L * 60L)
119
120 #endif /* HAVE_KRB4 */
121
122 #if defined(HAVE_KRB4)
123 extern C_Block  serv_key;
124 extern Sched    serv_ksched;
125 #endif
126 #if defined(HAVE_KRB5) && !defined(HAVE_KRB4)
127 krb5_keyblock   *server_key;
128 #endif
129
130 static Timer *bdump_timer;
131 static int live_socket = -1;
132 static FILE *input, *output;
133 static struct sockaddr_in bdump_sin;
134 #ifdef HAVE_KRB5
135 static krb5_auth_context bdump_ac;
136 #endif
137
138 int bdumping;
139 int bdump_concurrent;
140 extern char *bdump_version;
141 extern int bdump_auth_proto;
142
143 /*
144  * Functions for performing a brain dump between servers.
145  */
146
147 /*
148  * offer the brain dump to another server
149  */
150
151 void
152 bdump_offer(struct sockaddr_in *who)
153 {
154     Code_t retval;
155     char buf[512], *addr, *lyst[2];
156 #if !defined(HAVE_KRB4) && !defined(HAVE_KRB5)
157     int bdump_port = IPPORT_RESERVED - 1;
158 #endif /* !HAVE_KRB4 */
159
160     zdbug((LOG_DEBUG, "bdump_offer"));
161
162 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
163     /* 
164      * when using kerberos server-server authentication, we can
165      * use any random local address 
166      */
167     bdump_socket = socket(AF_INET, SOCK_STREAM, 0);
168     if (bdump_socket < 0) {
169         syslog(LOG_ERR,"bdump_offer: socket: %m");
170         bdump_socket = -1;
171         return;
172     }
173     memset(&bdump_sin, 0, sizeof(bdump_sin));
174     /* a port field of 0 makes the UNIX
175      * kernel choose an appropriate port/address pair */
176  
177     bdump_sin.sin_port = 0;
178     bdump_sin.sin_addr = my_addr;
179     bdump_sin.sin_family = AF_INET;
180     retval = bind(bdump_socket, (struct sockaddr *) &bdump_sin,
181                   sizeof(bdump_sin));
182     if (retval < 0) {
183         syslog(LOG_ERR, "bdump_offer: bind: %m");
184         close(bdump_socket);
185         bdump_socket = -1;
186         return;
187     }
188     if (!bdump_sin.sin_port) {
189         unsigned int len = sizeof(bdump_sin);
190
191         if (getsockname(bdump_socket,
192                         (struct sockaddr *) &bdump_sin, &len) < 0) {
193             syslog(LOG_ERR, "bdump_offer: getsockname: %m");
194             close(bdump_socket);
195             bdump_socket = -1;
196             return;
197         }
198     }
199 #else  /* !HAVE_KRB4 */
200     /*
201      * when not using HAVE_KRB4, we can't use any old port, we use
202      * Internet reserved ports instead (rresvport)
203      */
204     bdump_socket = rresvport(&bdump_port);
205     if (bdump_socket < 0) {
206         syslog(LOG_ERR,"bdump_offer: socket: %m");
207         bdump_socket = -1;
208         return;
209     }
210     memset(&bdump_sin, 0, sizeof(bdump_sin));
211     bdump_sin.sin_port = htons((unsigned short) bdump_port);
212     bdump_sin.sin_addr = my_addr;
213     bdump_sin.sin_family = AF_INET;
214 #endif                          /* HAVE_KRB4 */
215
216     listen(bdump_socket, 1);
217  
218     bdump_timer = timer_set_rel(20L, close_bdump, NULL);
219     FD_SET(bdump_socket, &interesting);
220     nfds = max(bdump_socket, srv_socket) + 1;
221
222     addr = inet_ntoa(bdump_sin.sin_addr);
223     sprintf(buf, "%d", ntohs(bdump_sin.sin_port));
224     lyst[0] = addr;
225     lyst[1] = buf;
226  
227     retval = ZSetDestAddr(who);
228     if (retval != ZERR_NONE) {
229         syslog(LOG_WARNING, "bdump_offer: ZSetDestAddr: %s",
230                error_message(retval));
231         return;
232     }
233  
234     /* myname is the hostname */
235     /* the class instance is the version number, here it is */
236     /* bdump_version, which is set in main */
237     send_list(ACKED, srv_addr.sin_port, ZEPHYR_ADMIN_CLASS, bdump_version,
238               ADMIN_BDUMP, myname, "", lyst, 2);
239         
240     zdbug((LOG_DEBUG,"bdump_offer: address is %s/%d\n",
241            inet_ntoa(bdump_sin.sin_addr),
242            ntohs(bdump_sin.sin_port)));
243     return;
244 }
245
246 /*
247  * Accept a connection, and send the brain dump to the other server
248  */
249
250 void
251 bdump_send(void)
252 {
253     struct sockaddr_in from;
254     Server *server;
255     Code_t retval;
256     unsigned int fromlen = sizeof(from);
257     int on = 1;
258 #ifdef _POSIX_VERSION
259     struct sigaction action;
260 #endif
261 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
262     char *data = NULL;
263     int len = 0;
264     int proto = 0;
265 #endif
266 #ifdef HAVE_KRB4
267     KTEXT_ST ticket;
268     AUTH_DAT kdata;
269     /* may be moved into kstuff.c */
270     char instance [INST_SZ];
271 #endif
272 #ifdef HAVE_KRB5
273     /* may be moved into kstuff.c */
274     krb5_principal principal;
275     krb5_data k5data;
276     krb5_keytab kt;
277 #endif
278 #if !defined(HAVE_KRB4) && !defined(HAVE_KRB5)
279     unsigned short fromport;
280 #endif /* HAVE_KRB4 */
281  
282     zdbug((LOG_DEBUG, "bdump_send"));
283
284     /* accept the connection, and send the brain dump */
285     live_socket = accept(bdump_socket, (struct sockaddr *) &from, &fromlen);
286     if (live_socket < 0) {
287         syslog(LOG_ERR,"bdump_send: accept: %m");
288         return;
289     }
290     if (setsockopt(live_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
291                    sizeof(on)) < 0)
292         syslog(LOG_WARNING, "bdump_send: setsockopt (SO_KEEPALIVE): %m");
293  
294 #if !defined(HAVE_KRB4) && !defined(HAVE_KRB5)
295     fromport = ntohs(from.sin_port);
296 #endif
297  
298 #ifdef _POSIX_VERSION
299     sigemptyset(&action.sa_mask);
300     action.sa_flags = 0;
301     action.sa_handler = SIG_IGN;
302     sigaction(SIGPIPE, &action, NULL);
303
304 #else
305     signal(SIGPIPE, SIG_IGN);   /* so we can detect failures */
306 #endif
307  
308     from.sin_port = srv_addr.sin_port; /* we don't care what port
309                                         * it came from, and we need to
310                                         * fake out server_which_server() */
311     server = server_which_server(&from);
312     if (!server) {
313         syslog(LOG_ERR, "bdump_send: unknown server?");
314         server = limbo_server;
315     }
316
317     zdbug((LOG_DEBUG, "bdump_send: connection from %s/%d",
318            inet_ntoa(from.sin_addr), ntohs(from.sin_port)));
319
320     bdumping = 1;
321     server->dumping = 1;
322
323     if (bdump_socket >= 0) {
324         /* shut down the listening socket and the timer. */
325         FD_CLR(bdump_socket, &interesting);
326         close(bdump_socket);
327         nfds = srv_socket + 1;
328         bdump_socket = -1;
329         timer_reset(bdump_timer);
330     }
331
332     /* Now begin the brain dump. */
333 #if defined(HAVE_KRB5) || defined(HAVE_KRB4)
334     retval = ReadKerberosData(live_socket, &len, &data, &proto);
335
336     if (retval != 0) {
337         syslog(LOG_ERR, "bdump_send: ReadKerberosData: %s",
338                error_message(retval));
339         cleanup(server);
340         return;
341     }
342
343     syslog(LOG_INFO, "bdump_send: got %d bytes of authenticator for protocol %d", len, proto);
344
345     if (get_tgt()) {
346         syslog(LOG_ERR, "bdump_send: get_tgt failed");
347         cleanup(server);
348         return;
349     }
350  
351     switch(proto) {
352 #ifdef HAVE_KRB5
353     case 5:
354         /* "server" side */
355         retval = krb5_build_principal(Z_krb5_ctx, &principal, 
356                                       strlen(ZGetRealm()),
357                                       ZGetRealm(),
358                                       SERVER_KRB5_SERVICE, SERVER_INSTANCE,
359                                       0); 
360         if (retval) {
361             syslog(LOG_ERR, "bdump_send: krb5_build_principal: %s", error_message(retval));
362             cleanup(server);
363             return;
364         }
365         
366
367         retval = krb5_auth_con_init(Z_krb5_ctx, &bdump_ac);
368         if (retval) {
369             syslog(LOG_ERR, "bdump_send: krb5_auth_con_init: %s", error_message(retval));
370             cleanup(server);
371             return;
372         }
373
374         retval = krb5_auth_con_setflags(Z_krb5_ctx, bdump_ac, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
375         if (retval) {
376             syslog(LOG_ERR, "bdump_send: krb5_auth_con_setflags: %s", error_message(retval));
377             cleanup(server);
378             return;
379         }
380
381         retval = krb5_auth_con_genaddrs(Z_krb5_ctx, bdump_ac, live_socket,
382                                        KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR|KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR);
383         if (retval) {
384             syslog(LOG_ERR, "bdump_send: krb5_auth_con_genaddrs: %s", error_message(retval));
385             cleanup(server);
386             return;
387         }
388
389         /* Get the "client" krb_ap_req */
390
391         memset((char *)&k5data, 0, sizeof(krb5_data));
392         k5data.length = len;
393         k5data.data = data;
394
395         /* resolve keytab */
396         retval = krb5_kt_resolve(Z_krb5_ctx, keytab_file, &kt);
397         if (retval) {
398             syslog(LOG_ERR, "bdump_send: cannot resolve keytab: %s", 
399                    error_message(retval));
400             krb5_kt_close(Z_krb5_ctx, kt);
401             cleanup(server);
402             return;
403         }
404
405         retval = krb5_rd_req(Z_krb5_ctx, &bdump_ac, &k5data, principal, kt, NULL, NULL);
406         krb5_free_principal(Z_krb5_ctx, principal);
407         krb5_kt_close(Z_krb5_ctx, kt);
408         free(k5data.data);
409         memset((char *)&k5data, 0, sizeof(krb5_data));
410         if (retval) {
411              syslog(LOG_ERR, "bdump_send: mutual authentication failed: %s",
412                     error_message(retval));
413              cleanup(server);
414              return;
415         }
416
417         /* Now send back our auth packet */
418
419         retval = krb5_mk_rep(Z_krb5_ctx, bdump_ac, &k5data);
420         if (retval) {
421             syslog(LOG_ERR, "bdump_send: krb5_mk_rep: %s", error_message(retval));
422             cleanup(server);
423             return;
424         }
425         retval = SendKrb5Data(live_socket, &k5data);
426         if (retval) {
427              syslog(LOG_ERR, "bdump_send: cannot send authenticator: %s",
428                     error_message(retval));
429              krb5_free_data_contents(Z_krb5_ctx, &k5data);
430              cleanup(server);
431              return;
432         }    
433         krb5_free_data_contents(Z_krb5_ctx, &k5data);
434         break;
435 #endif  /* HAVE_KRB5 */  
436 #ifdef HAVE_KRB4
437     case 4:
438         /* here to krb_rd_req from GetKerberosData candidate for refactoring
439            back into kstuff.c */
440         (void) strcpy(instance, "*");           /* let Kerberos fill it in */
441
442         ticket.length = len;
443         memcpy(&ticket.dat, data, MIN(len, sizeof(ticket.dat)));
444         retval = krb_rd_req(&ticket, SERVER_SERVICE, instance,
445                             from.sin_addr.s_addr, &kdata, srvtab_file);
446         /*
447         retval = GetKerberosData(live_socket, from.sin_addr, &kdata,
448                                  SERVER_SERVICE, srvtab_file);
449         */
450         if (retval != KSUCCESS) {
451             syslog(LOG_ERR, "bdump_send: getkdata: %s",
452                    error_message(retval));
453             cleanup(server);
454             return;
455         }
456         if (strcmp(kdata.pname, SERVER_SERVICE) ||
457             strcmp(kdata.pinst, SERVER_INSTANCE) ||
458             strcmp(kdata.prealm, ZGetRealm())) {
459             syslog(LOG_ERR, "bdump_send: peer not zephyr: %s.%s@%s",
460                    kdata.pname, kdata.pinst, kdata.prealm);
461             cleanup(server);
462             return;
463         }
464         /* authenticate back */
465         retval = SendKerberosData(live_socket, &ticket, SERVER_SERVICE,
466                                   SERVER_INSTANCE);
467         if (retval != 0) {
468             syslog(LOG_ERR,"bdump_send: SendKerberosData: %s",
469                    error_message (retval));
470             cleanup(server);
471             return;
472         }
473         break;
474 #endif /* HAVE_KRB4 */
475     }
476 #else /* HAVE_KRB4 || HAVE_KRB5 */
477     if (fromport > IPPORT_RESERVED || fromport < IPPORT_RESERVED / 2) {
478         syslog(LOG_ERR, "bdump_send: bad port from peer: %d", fromport);
479         cleanup(server);
480         return;
481     }
482 #endif /* HAVE_KRB4 || HAVE_KRB5 */
483     retval = setup_file_pointers();
484     if (retval != 0) {
485         syslog (LOG_WARNING, "bdump_send: can't set up file pointers: %s",
486                 error_message(retval));
487         cleanup(server);
488         return;
489     }
490     retval = bdump_send_loop(server);
491     if (retval != ZERR_NONE) {
492         syslog(LOG_WARNING, "bdump_send: bdump_send_loop failed: %s",
493                error_message(retval));
494         cleanup(server);
495         return;
496     }
497     retval = bdump_recv_loop(server);
498     if (retval != ZERR_NONE) {
499         syslog(LOG_WARNING, "bdump_send: bdump_recv_loop failed: %s",
500                error_message(retval));
501         cleanup(server);
502         return;
503     }
504
505     zdbug((LOG_DEBUG, "bdump_send: finished"));
506
507     if (server != limbo_server) {
508         /* set this guy to be up, and schedule a hello */
509         server->state = SERV_UP;
510         timer_reset(server->timer);
511         server->timer = timer_set_rel(0L, server_timo, server);
512     }
513
514     shutdown_file_pointers();
515
516 #ifdef _POSIX_VERSION
517     action.sa_handler = SIG_DFL;
518     sigaction(SIGPIPE, &action, NULL);
519 #else
520     signal(SIGPIPE, SIG_DFL);
521 #endif
522     bdumping = 0;
523     server->dumping = 0;
524     /* Now that we are finished dumping, send all the queued packets */
525     server_send_queue(server);
526     return;
527 }
528
529 /*ARGSUSED*/
530 static void
531 bdump_get_v12 (ZNotice_t *notice,
532                int auth,
533                struct sockaddr_in *who,
534                Server *server)
535 {
536     struct sockaddr_in from;
537     Code_t retval;
538     int on = 1;
539 #ifdef _POSIX_VERSION
540     struct sigaction action;
541 #endif
542 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
543 #ifdef HAVE_KRB5
544     krb5_creds creds;
545     krb5_creds *credsp;
546     krb5_principal principal;
547     krb5_data data;
548     krb5_ap_rep_enc_part *rep;
549 #endif
550 #ifdef HAVE_KRB4
551     KTEXT_ST ticket;
552     AUTH_DAT kdata;
553 #endif
554 #else  /* !HAVE_KRB4 && !HAVE_KRB5 */
555     int reserved_port = IPPORT_RESERVED - 1;
556 #endif /* !HAVE_KRB4 && !HAVE_KRB5 */
557     
558     bdumping = 1;
559     server->dumping = 1;
560  
561 #ifdef _POSIX_VERSION
562     action.sa_flags = 0;
563     sigemptyset(&action.sa_mask);
564     action.sa_handler = SIG_IGN;
565     sigaction(SIGPIPE, &action, NULL);
566 #else
567     signal(SIGPIPE, SIG_IGN);   /* so we can detect problems */
568 #endif /* _POSIX_VRESION */
569  
570     if (bdump_socket >= 0) {
571         /* We cannot go get a brain dump when someone may
572            potentially be connecting to us (if that other
573            server is the server to whom we are connecting,
574            we will deadlock. so we shut down the listening
575            socket and the timer. */
576         FD_CLR(bdump_socket, &interesting);
577         close(bdump_socket);
578         nfds = srv_socket+1;
579         bdump_socket = -1;
580         timer_reset(bdump_timer);
581     }
582
583     retval = extract_sin(notice, &from);
584     if (retval != ZERR_NONE) {
585         syslog(LOG_ERR, "bdump_get: sin: %s", error_message(retval));
586 #ifdef _POSIX_VERSION
587         action.sa_handler = SIG_DFL;
588         sigaction(SIGPIPE, &action, NULL);
589 #else
590         signal(SIGPIPE, SIG_DFL);
591 #endif
592         bdumping = 0;
593         server->dumping = 0;
594         return;
595     }
596 #if !defined(HAVE_KRB4) && !defined(HAVE_KRB5)
597     if (ntohs(from.sin_port) > IPPORT_RESERVED ||
598         ntohs(from.sin_port) < IPPORT_RESERVED / 2) {
599         syslog(LOG_ERR, "bdump_get: port not reserved: %d",
600                ntohs(from.sin_port));
601         cleanup(server);
602         return;
603     }
604     live_socket = rresvport(&reserved_port);
605 #else  /* !HAVE_KRB4 && !HAVE_KRB5 */
606     live_socket = socket(AF_INET, SOCK_STREAM, 0);
607 #endif /* !HAVE_KRB4 && !HAVE_KRB5 */
608     if (live_socket < 0) {
609         syslog(LOG_ERR, "bdump_get: socket: %m");
610         cleanup(server);
611         return;
612     }
613     if (connect(live_socket, (struct sockaddr *) &from, sizeof(from))) {
614         syslog(LOG_ERR, "bdump_get: connect: %m");
615         cleanup(server);
616         return;
617     }
618     if (setsockopt(live_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
619                    sizeof(on)) < 0)
620         syslog(LOG_WARNING, "bdump_get: setsockopt (SO_KEEPALIVE): %m");
621
622     zdbug((LOG_DEBUG, "bdump_get: connected"));
623  
624     /* Now begin the brain dump. */
625 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
626     if (get_tgt()) {
627         syslog(LOG_ERR, "bdump_get: get_tgt failed"); 
628         cleanup(server);
629         return;
630     }
631     switch(bdump_auth_proto) {
632 #ifdef HAVE_KRB5
633     case 5: /* "client" side */
634         memset((char *)&creds, 0, sizeof(creds));
635
636         retval = krb5_build_principal(Z_krb5_ctx, &principal, 
637                                       strlen(ZGetRealm()),
638                                       ZGetRealm(),
639                                       SERVER_KRB5_SERVICE, SERVER_INSTANCE,
640                                       0); 
641         if (retval) {
642             syslog(LOG_ERR, "bdump_get: krb5_build_principal: %s", error_message(retval));
643             cleanup(server);
644             return;
645         }
646
647         retval = krb5_copy_principal(Z_krb5_ctx, principal, &creds.server);
648         if (retval) {
649             syslog(LOG_ERR, "bdump_get: krb5_copy_principal (server): %s", error_message(retval));
650             krb5_free_principal(Z_krb5_ctx, principal);
651             cleanup(server);
652             return;
653         }
654         
655         retval = krb5_copy_principal(Z_krb5_ctx, principal, &creds.client);
656         krb5_free_principal(Z_krb5_ctx, principal);
657         if (retval) {
658             syslog(LOG_ERR, "bdump_get: krb5_copy_principal (client): %s", error_message(retval));
659             krb5_free_cred_contents(Z_krb5_ctx, &creds);
660             cleanup(server);
661             return;
662         }
663
664         retval = krb5_get_credentials(Z_krb5_ctx, 0, Z_krb5_ccache,
665                                       &creds, &credsp);
666         krb5_free_cred_contents(Z_krb5_ctx, &creds);
667         if (retval) {
668             syslog(LOG_ERR, "bdump_get: krb5_get_credentials: %s", error_message(retval));
669             cleanup(server);
670             return;
671         }
672
673         retval = krb5_auth_con_init(Z_krb5_ctx, &bdump_ac);
674         if (retval) {
675             syslog(LOG_ERR, "bdump_get: krb5_auth_con_init: %s", error_message(retval));
676             krb5_free_creds(Z_krb5_ctx, credsp);
677             cleanup(server);
678             return;
679         }
680
681         retval = krb5_auth_con_setflags(Z_krb5_ctx, bdump_ac, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
682         if (retval) {
683             syslog(LOG_ERR, "bdump_get: krb5_auth_con_setflags: %s", error_message(retval));
684             krb5_free_creds(Z_krb5_ctx, credsp);
685             cleanup(server);
686             return;
687         }
688
689         retval = krb5_auth_con_genaddrs(Z_krb5_ctx, bdump_ac, live_socket,
690                 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR|KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR);
691         if (retval) {
692             syslog(LOG_ERR, "bdump_get: krb5_auth_con_genaddrs: %s", error_message(retval));
693             krb5_free_creds(Z_krb5_ctx, credsp);
694             cleanup(server);
695             return;
696         }
697
698         memset((char *)&data, 0, sizeof(krb5_data));
699         retval = krb5_mk_req_extended(Z_krb5_ctx, &bdump_ac, AP_OPTS_MUTUAL_REQUIRED|AP_OPTS_USE_SUBKEY,
700                                  NULL, credsp, &data);
701         if (retval) {
702             syslog(LOG_ERR, "bdump_get: krb5_mk_req_ext: %s", error_message(retval));
703             krb5_free_creds(Z_krb5_ctx, credsp);
704             cleanup(server);
705             return;
706         }
707         retval = SendKrb5Data(live_socket, &data);
708         krb5_free_creds(Z_krb5_ctx, credsp);
709         if (retval) {
710              syslog(LOG_ERR, "bdump_get: cannot send authenticator: %s",
711                     error_message(retval));
712              krb5_free_data_contents(Z_krb5_ctx, &data);
713              cleanup(server);
714              return;
715         }    
716         krb5_free_data_contents(Z_krb5_ctx, &data);
717         memset((char *)&data, 0, sizeof(krb5_data));
718         retval = GetKrb5Data(live_socket, &data);
719         if (retval) {
720              syslog(LOG_ERR, "bdump_get: cannot get auth response: %s",
721                     error_message(retval));
722              cleanup(server);
723              return;
724         }    
725         retval = krb5_rd_rep(Z_krb5_ctx, bdump_ac, &data, &rep);
726         free(data.data);
727         memset((char *)&data, 0, sizeof(krb5_data));
728         if (retval) {
729              syslog(LOG_ERR, "bdump_get: mutual authentication failed: %s",
730                     error_message(retval));
731              cleanup(server);
732              return;
733         }    
734         break;
735 #endif
736 #ifdef HAVE_KRB4
737     case 4:
738         /* send an authenticator */
739         retval = SendKerberosData(live_socket, &ticket, SERVER_SERVICE,
740                                   SERVER_INSTANCE);
741         if (retval != 0) {
742             syslog(LOG_ERR,"bdump_get: %s", error_message(retval));
743             cleanup(server);
744             return;
745         }
746         zdbug((LOG_DEBUG, "bdump_get: SendKerberosData ok"));
747         
748         /* get his authenticator */
749         retval = GetKerberosData(live_socket, from.sin_addr, &kdata,
750                                  SERVER_SERVICE, srvtab_file);
751         if (retval != KSUCCESS) {
752             syslog(LOG_ERR, "bdump_get getkdata: %s",error_message(retval));
753             cleanup(server);
754             return;
755         }
756
757         if (strcmp(kdata.pname, SERVER_SERVICE) ||
758             strcmp(kdata.pinst, SERVER_INSTANCE) ||
759             strcmp(kdata.prealm, ZGetRealm())) {
760             syslog(LOG_ERR, "bdump_get: peer not zephyr in lrealm: %s.%s@%s",
761                    kdata.pname, kdata.pinst,kdata.prealm);
762             cleanup(server);
763             return;
764         }
765         break;
766 #endif /* HAVE_KRB4 */
767     }
768 #endif /* defined(HAVE_KRB4) || defined(HAVE_KRB5) */   
769     retval = setup_file_pointers();
770     if (retval != 0) {
771         syslog(LOG_WARNING, "bdump_get: can't set up file pointers: %s",
772                error_message (retval));
773         cleanup(server);
774         return;
775     }
776     retval = bdump_recv_loop(server);
777     if (retval != ZERR_NONE) {
778         syslog(LOG_WARNING, "bdump_get: bdump_recv_loop failed: %s",
779                error_message(retval));
780         cleanup(server);
781         return;
782     }
783     zdbug((LOG_DEBUG,"bdump_get: gbdl ok"));
784     retval = bdump_send_loop(server);
785     if (retval != ZERR_NONE) {
786         syslog(LOG_WARNING, "bdump_send_loop failed: %s",
787                error_message(retval));
788         cleanup(server);
789         return;
790     }
791
792     zdbug((LOG_DEBUG, "bdump_get: gbd finished"));
793
794     /* set this guy to be up, and schedule a hello */
795     server->state = SERV_UP;
796     timer_reset(server->timer);
797     server->timer = timer_set_rel(0L, server_timo, server);
798
799
800     zdbug((LOG_DEBUG,"cleanup gbd"));
801
802     shutdown_file_pointers();
803 #ifdef _POSIX_VERSION
804     action.sa_handler = SIG_DFL;
805     sigaction(SIGPIPE, &action, NULL);
806 #else
807     signal(SIGPIPE, SIG_DFL);
808 #endif
809     bdumping = 0;
810     server->dumping = 0;
811     /* Now that we are finished dumping, send all the queued packets */
812     server_send_queue(server);
813
814     return;
815 }
816
817 void
818 bdump_get(ZNotice_t *notice,
819           int auth,
820           struct sockaddr_in *who,
821           Server *server)
822 {
823     void (*proc)(ZNotice_t *, int, struct sockaddr_in *, Server *);
824
825     proc = NULL;
826
827     if (zdebug) {
828         syslog(LOG_DEBUG, "bdump_get: bdump v%s avail %s",
829                notice->z_class_inst, inet_ntoa(who->sin_addr));
830     }
831
832     if (strcmp (notice->z_class_inst, "1.2") == 0)
833         proc = bdump_get_v12;
834
835     if (proc) {
836         (*proc)(notice, auth, who, server);
837     } else {
838         syslog(LOG_WARNING,
839                "bdump_get: Incompatible bdump version '%s' from %s",
840                notice->z_class_inst,
841                inet_ntoa(who->sin_addr));
842     }
843 }
844
845 /*
846  * Send a list off as the specified notice
847  */
848
849 Code_t
850 bdump_send_list_tcp(ZNotice_Kind_t kind,
851                     struct sockaddr_in *addr,
852                     char *class_name,
853                     char *inst,
854                     char *opcode,
855                     char *sender,
856                     char *recip,
857                     char **lyst,
858                     int num)
859 {
860     ZNotice_t notice;
861     char *pack, addrbuf[100];
862     int packlen, count;
863     Code_t retval;
864     u_short length;
865
866     memset (&notice, 0, sizeof(notice));
867  
868     notice.z_kind = kind;
869  
870     notice.z_port = addr->sin_port;
871     notice.z_class = class_name;
872     notice.z_class_inst = inst;
873     notice.z_opcode = opcode;
874     notice.z_sender = sender;
875     notice.z_recipient = recip;
876     notice.z_default_format = "";
877     notice.z_num_other_fields = 0;
878     if (addr)
879         notice.z_sender_sockaddr.ip4 = *addr; /*XXX*/
880  
881     retval = ZFormatNoticeList(&notice, lyst, num, &pack, &packlen, ZNOAUTH);
882     if (retval != ZERR_NONE)
883         return retval;
884
885 #ifdef HAVE_KRB5
886     if (bdump_ac) {
887         krb5_data indata, outmsg;
888         indata.length=packlen;
889         indata.data=pack;
890         memset(&outmsg, 0, sizeof(krb5_data));
891         retval = krb5_mk_priv(Z_krb5_ctx, bdump_ac, &indata, &outmsg, NULL);
892         if (retval != ZERR_NONE)
893             return retval;
894         if (outmsg.length > Z_MAXPKTLEN) {
895             syslog(LOG_ERR, "bsl: encrypted packet is too large");
896             return ZERR_PKTLEN;
897         }
898         packlen = outmsg.length;
899         free(pack);
900         pack=malloc(packlen);
901         if (!pack)
902             return ENOMEM;
903         memcpy(pack, outmsg.data, packlen);
904         krb5_free_data_contents(Z_krb5_ctx, &outmsg);
905     }
906 #endif
907
908     length = htons((u_short) packlen);
909  
910     count = net_write(output, (char *) &length, sizeof(length));
911     if (count != sizeof(length)) {
912         if (count < 0) {
913             free(pack);
914             return(errno);
915         } else {
916             syslog(LOG_WARNING, "slt (length) xmit: %d vs %d",
917                    sizeof(length), count);
918             free(pack);
919             return(ZSRV_PKSHORT);
920         }
921     }
922  
923     count = net_write(output, pack, packlen);
924     if (count != packlen) {
925         if (count < 0) {
926             free(pack);
927             return(errno);
928         } else {
929             syslog(LOG_WARNING, "slt (packet) xmit: %d vs %d",
930                    packlen, count);
931             free(pack);
932             return(ZSRV_PKSHORT);
933         }
934     }
935     free(pack);
936     return(ZERR_NONE);
937 }
938
939 static void
940 shutdown_file_pointers(void)
941 {
942     if (input) {
943         fclose(input);
944         input = 0;
945     }
946     if (output) {
947         fclose(output);
948         output = 0;
949     }
950     if (live_socket >= 0) {
951         close(live_socket);
952         live_socket = -1;
953 #ifdef HAVE_KRB5
954         if (bdump_ac)
955                 krb5_auth_con_free(Z_krb5_ctx, bdump_ac);
956         bdump_ac = NULL;
957 #endif
958     }
959 }
960
961 static void
962 cleanup(Server *server)
963 {
964 #ifdef _POSIX_VERSION
965     struct sigaction action;
966 #endif
967
968     zdbug((LOG_DEBUG, "bdump cleanup"));
969
970     if (server != limbo_server) {
971         server->state = SERV_DEAD;
972         timer_reset(server->timer);
973         server->timer = timer_set_rel(0L, server_timo, server);
974     }
975     shutdown_file_pointers ();
976 #ifdef _POSIX_VERSION
977     action.sa_flags = 0;
978     sigemptyset(&action.sa_mask);
979     action.sa_handler = SIG_DFL;
980     sigaction(SIGPIPE,&action, NULL);
981 #else
982     signal(SIGPIPE, SIG_DFL);
983 #endif /* _POSIX_VERSION */
984     bdumping = 0;
985     server->dumping = 0;
986 }
987
988 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
989
990 int got_des = 0;
991
992 #ifndef HAVE_KRB4
993 unsigned int enctypes[] = {ENCTYPE_DES_CBC_CRC,
994                            ENCTYPE_DES_CBC_MD4,
995                            ENCTYPE_DES_CBC_MD5,
996 #ifdef ENCTYPE_DES_CBC_RAW
997                            ENCTYPE_DES_CBC_RAW,
998 #endif
999                            0};
1000 #endif
1001
1002
1003 int
1004 get_tgt(void)
1005 {
1006     int retval = 0;
1007 #ifndef HAVE_KRB4
1008     int i;
1009     krb5_keytab_entry kt_ent;
1010 #endif
1011 #ifdef HAVE_KRB4
1012     /* MIT Kerberos 4 get_svc_in_tkt() requires instance to be writable and
1013      * at least INST_SZ bytes long. */
1014     static char buf[INST_SZ + 1] = SERVER_INSTANCE;
1015
1016     /* have they expired ? */
1017     if (ticket_time < NOW - tkt_lifetime(TKTLIFETIME) + (15L * 60L)) {
1018         /* +15 for leeway */
1019
1020         zdbug((LOG_DEBUG,"get new tickets: %d %d %d", ticket_time, NOW,
1021                NOW - tkt_lifetime(TKTLIFETIME) + 15L));
1022
1023         dest_tkt();
1024
1025         retval = krb_get_svc_in_tkt(SERVER_SERVICE, buf, (char *)ZGetRealm(),
1026                                     "krbtgt", (char *)ZGetRealm(),
1027                                     TKTLIFETIME, srvtab_file);
1028         if (retval != KSUCCESS) {
1029             syslog(LOG_ERR,"get_tgt: krb_get_svc_in_tkt: %s",
1030                    error_message(retval));
1031             ticket_time = 0;
1032             return(1);
1033         } else {
1034             ticket_time = NOW;
1035         }
1036
1037         retval = read_service_key(SERVER_SERVICE, SERVER_INSTANCE,
1038                                   (char *)ZGetRealm(), 0 /*kvno*/,
1039                                   srvtab_file, (char *)serv_key);
1040         if (retval != KSUCCESS) {
1041             syslog(LOG_ERR, "get_tgt: read_service_key: %s",
1042                    error_message(retval));
1043             return 1;
1044         }
1045         des_key_sched(serv_key, serv_ksched.s);
1046         got_des = 1;
1047     }
1048 #endif
1049 #ifdef HAVE_KRB5        
1050     /* XXX */
1051     if (ticket5_time < NOW - tkt5_lifetime(TKT5LIFETIME) + (15L * 60L)) {
1052         krb5_keytab kt;
1053         krb5_get_init_creds_opt opt;
1054         krb5_creds cred;
1055         krb5_principal principal;
1056
1057         memset(&cred, 0, sizeof(cred));
1058
1059         retval = krb5_build_principal(Z_krb5_ctx, &principal, 
1060                                       strlen(ZGetRealm()),
1061                                       ZGetRealm(),
1062                                       SERVER_KRB5_SERVICE, SERVER_INSTANCE,
1063                                       0); 
1064         if (retval) {
1065           krb5_free_principal(Z_krb5_ctx, principal);
1066           return(1);
1067         }
1068
1069         krb5_get_init_creds_opt_init (&opt);
1070         krb5_get_init_creds_opt_set_tkt_life (&opt, TKT5LIFETIME);
1071
1072         retval = krb5_kt_resolve(Z_krb5_ctx, keytab_file, &kt);
1073         if (retval) return(1);
1074         
1075         retval = krb5_get_init_creds_keytab (Z_krb5_ctx,
1076                                              &cred,
1077                                              principal,
1078                                              kt,
1079                                              0,
1080                                              NULL,
1081                                              &opt);
1082 #ifndef HAVE_KRB4
1083         if (retval) {
1084             krb5_free_principal(Z_krb5_ctx, principal);
1085             krb5_kt_close(Z_krb5_ctx, kt);
1086             return(1);
1087         }
1088
1089         for (i = 0; enctypes[i]; i++) {
1090             retval = krb5_kt_get_entry(Z_krb5_ctx, kt, principal,
1091                                        0, enctypes[i], &kt_ent);
1092             if (!retval)
1093                 break;
1094         }
1095         if (!retval) {
1096 #ifdef HAVE_KRB5_CRYPTO_INIT
1097             retval = krb5_copy_keyblock(Z_krb5_ctx, &kt_ent.keyblock,
1098                                         &server_key);
1099 #else
1100             retval = krb5_copy_keyblock(Z_krb5_ctx, &kt_ent.key, &server_key);
1101 #endif
1102             if (retval) {
1103                 krb5_free_principal(Z_krb5_ctx, principal);
1104                 krb5_kt_close(Z_krb5_ctx, kt);
1105                 return(1);
1106             }
1107         
1108             got_des = 1;
1109         }
1110 #endif
1111         krb5_free_principal(Z_krb5_ctx, principal);
1112         krb5_kt_close(Z_krb5_ctx, kt);
1113 #ifndef HAVE_KRB4
1114         if (retval) return(1);
1115 #endif
1116
1117         retval = krb5_cc_initialize (Z_krb5_ctx, Z_krb5_ccache, cred.client);
1118         if (retval) return(1);
1119     
1120         retval = krb5_cc_store_cred (Z_krb5_ctx, Z_krb5_ccache, &cred);
1121         if (retval) return(1);
1122
1123         ticket5_time = NOW;
1124
1125         krb5_free_cred_contents (Z_krb5_ctx, &cred);
1126     }
1127 #endif
1128     return(0);
1129 }
1130 #endif /* HAVE_KRB4 */
1131
1132 /*
1133  * The braindump offer wasn't taken, so we retract it.
1134  */
1135  
1136 /*ARGSUSED*/
1137 static void
1138 close_bdump(void *arg)
1139 {
1140     if (bdump_socket >= 0) {
1141         FD_CLR(bdump_socket, &interesting);
1142         close(bdump_socket);
1143         nfds = srv_socket + 1;
1144         bdump_socket = -1;
1145
1146         zdbug((LOG_DEBUG, "bdump not used"));
1147     } else {
1148         zdbug((LOG_DEBUG, "bdump not open"));
1149     }
1150     return;
1151 }
1152  
1153 /*
1154  * Start receiving instruction notices from the brain dump socket
1155  */
1156  
1157 static Code_t
1158 bdump_recv_loop(Server *server)
1159 {
1160     ZNotice_t notice;
1161     ZPacket_t packet;
1162     int len;
1163     Code_t retval;
1164     Client *client = NULL;
1165     struct sockaddr_in who;
1166 #ifdef HAVE_KRB5
1167     unsigned char buf[512];
1168     int blen;
1169 #endif
1170 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)    
1171     char *cp;
1172 #ifndef HAVE_KRB4
1173     unsigned char cblock[8];
1174 #else
1175     C_Block cblock;
1176 #endif
1177 #endif
1178     ZRealm *realm = NULL;
1179  
1180     zdbug((LOG_DEBUG, "bdump recv loop"));
1181         
1182     /* do the inverse of bdump_send_loop, registering stuff on the fly */
1183     while (1) {
1184         if (packets_waiting()) {
1185             /* A non-braindump packet is waiting; handle it. */
1186             bdumping = 0;
1187             bdump_concurrent = 1;
1188             handle_packet();
1189             bdump_concurrent = 0;
1190             bdumping = 1;
1191         }
1192         len = sizeof(packet);
1193         retval = get_packet(packet, len, &len);
1194         if (retval != ZERR_NONE) {
1195             syslog(LOG_ERR, "brl get pkt: %s", error_message(retval));
1196             return retval;
1197         }
1198
1199 #if HAVE_KRB5
1200         if (bdump_ac) {
1201             krb5_data in, out;
1202             in.length = len;
1203             in.data = packet;
1204             memset(&out, 0, sizeof(krb5_data));
1205             retval = krb5_rd_priv(Z_krb5_ctx, bdump_ac, &in, &out, NULL);
1206             if (retval != ZERR_NONE) {
1207                 syslog(LOG_ERR, "brl krb5 rd priv: %s", error_message(retval));
1208                 return retval;
1209             }
1210             memcpy(packet, out.data, out.length);
1211             len = out.length;
1212             krb5_free_data_contents(Z_krb5_ctx, &out);
1213         }
1214 #endif
1215
1216         retval = ZParseNotice(packet, len, &notice);
1217         if (retval != ZERR_NONE) {
1218             syslog(LOG_ERR, "brl notice parse: %s", error_message(retval));
1219             return retval;
1220         }
1221 #if defined (DEBUG)
1222         if (zdebug) {
1223             syslog(LOG_DEBUG, "bdump:%s '%s' '%s' '%s' '%s' '%s'",
1224                     ZNoticeKinds[(int) notice.z_kind], notice.z_class,
1225                     notice.z_class_inst, notice.z_opcode, notice.z_sender,
1226                     notice.z_recipient);
1227         }
1228 #endif /* DEBUG */
1229         who.sin_family = AF_INET; /*XXX*/
1230         who.sin_addr.s_addr = notice.z_sender_sockaddr.ip4.sin_addr.s_addr;
1231         who.sin_port = notice.z_port;
1232
1233         if (strcmp(notice.z_opcode, ADMIN_DONE) == 0) {
1234             /* end of brain dump */
1235             return ZERR_NONE;
1236         } else if (strcmp(notice.z_opcode, ADMIN_NEWREALM) == 0) {
1237             /* get a realm from the message */
1238             realm = realm_get_realm_by_name(notice.z_message);
1239             if (!realm) {
1240                 syslog(LOG_ERR, "brl newrlm failed: no realm %s", 
1241                        notice.z_message);
1242             }
1243         } else if (strcmp(notice.z_class, LOGIN_CLASS) == 0) {
1244             /* 1 = tell it we are authentic */
1245             retval = ulogin_dispatch(&notice, 1, &who, server);
1246             if (retval != ZERR_NONE) {
1247                 syslog(LOG_ERR, "brl ul_disp failed: %s",
1248                        error_message(retval));
1249                 return retval;
1250             }
1251         } else if (strcmp(notice.z_opcode, ADMIN_NEWCLT) == 0) {
1252             /* a new client */
1253             notice.z_port = htons((u_short) atoi(notice.z_message));
1254             retval = client_register(&notice, &who.sin_addr, &client, 0);
1255             if (retval != ZERR_NONE) {
1256                 syslog(LOG_ERR,"brl failed: %s", error_message(retval));
1257                 return retval;
1258             }
1259 #ifdef HAVE_KRB5
1260             client->session_keyblock = NULL;
1261             if (*notice.z_class_inst) {
1262                 /* check out this session key I found */
1263                 cp = notice.z_message + strlen(notice.z_message) + 1;
1264                 if (*cp == '0' && got_des) {
1265                     /* ****ing netascii; this is an encrypted DES keyblock
1266                        XXX this code should be conditionalized for server
1267                        transitions   */
1268                     retval = Z_krb5_init_keyblock(Z_krb5_ctx, ENCTYPE_DES_CBC_CRC,
1269                                                   sizeof(cblock),
1270                                                   &client->session_keyblock);
1271                     if (retval) {
1272                         syslog(LOG_ERR, "brl failed to allocate DES keyblock: %s",
1273                                error_message(retval));
1274                         return retval;
1275                     }
1276                     retval = ZReadAscii(cp, strlen(cp), cblock, sizeof(cblock));
1277                     if (retval != ZERR_NONE) {
1278                         syslog(LOG_ERR,"brl bad cblk read: %s (%s)",
1279                                error_message(retval), cp);
1280                     } else {
1281                         retval = des_service_decrypt(cblock, Z_keydata(client->session_keyblock));
1282                         if (retval) {
1283                             syslog(LOG_ERR, "brl failed to decyrpt DES session key: %s",
1284                                    error_message(retval));
1285                             return retval;
1286                         }
1287                     }
1288                 } else if (*cp == 'Z') {
1289                     /* Zcode! Long live the new flesh! */
1290                     retval = ZReadZcode((unsigned char *)cp, buf, sizeof(buf), &blen);
1291                     if (retval != ZERR_NONE) {
1292                         syslog(LOG_ERR,"brl bad cblk read: %s (%s)",
1293                                error_message(retval), cp);
1294                     } else {
1295                         retval = Z_krb5_init_keyblock(Z_krb5_ctx,
1296                                                     ntohl(*(krb5_enctype *)&buf[0]),
1297                                                     ntohl(*(u_int32_t *)&buf[4]),
1298                                                     &client->session_keyblock);
1299                         if (retval) {
1300                             syslog(LOG_ERR, "brl failed to allocate keyblock: %s",
1301                                    error_message(retval));
1302                             return retval;
1303                         }
1304                         memcpy(Z_keydata(client->session_keyblock), &buf[8],
1305                                Z_keylen(client->session_keyblock));
1306                     }
1307                 }
1308             }
1309 #else
1310 #ifdef HAVE_KRB4
1311             memset(client->session_key, 0, sizeof(C_Block));
1312             if (*notice.z_class_inst) {
1313                 /* a C_Block is there */
1314                 cp = notice.z_message + strlen(notice.z_message) + 1;
1315                 retval = ZReadAscii(cp, strlen(cp), cblock, sizeof(C_Block));
1316                 if (retval != ZERR_NONE) {
1317                     syslog(LOG_ERR,"brl bad cblk read: %s (%s)",
1318                            error_message(retval), cp);
1319                 } else {
1320                     des_ecb_encrypt((des_cblock *)cblock,
1321                                     (des_cblock *)client->session_key,
1322                                     serv_ksched.s, DES_DECRYPT);
1323                 }
1324             }
1325 #endif /* HAVE_KRB4 */
1326 #endif
1327         } else if (strcmp(notice.z_opcode, CLIENT_SUBSCRIBE) == 0) { 
1328             /* a subscription packet */
1329             if (!client) {
1330                 syslog(LOG_ERR, "brl no client");
1331                 return ZSRV_NOCLT;
1332             }
1333             retval = subscr_subscribe(client, &notice, server);
1334             if (retval != ZERR_NONE) {
1335                 syslog(LOG_WARNING, "brl subscr failed: %s",
1336                        error_message(retval));
1337                 return retval;
1338             }
1339         } else if (strcmp(notice.z_opcode, REALM_SUBSCRIBE) == 0) {
1340             /* add a subscription for a realm */
1341             if (realm) {
1342                 retval = subscr_realm(realm, &notice);
1343                 if (retval != ZERR_NONE) {
1344                     syslog(LOG_WARNING, "brl subscr failed: %s",
1345                            error_message(retval));
1346                     return retval;
1347                 }
1348             } /* else */
1349                  /* Other side tried to send us subs for a realm we didn't
1350                     know about, and so we drop them silently */
1351         
1352         } else {
1353             syslog(LOG_ERR, "brl bad opcode %s",notice.z_opcode);
1354             return ZSRV_UNKNOWNOPCODE;
1355         }
1356     }
1357 }
1358
1359 /*
1360  * Send all the state to the peer.
1361  */
1362
1363 static Code_t
1364 bdump_send_loop(Server *server)
1365 {
1366     Code_t retval;
1367
1368     zdbug((LOG_DEBUG, "bdump send loop"));
1369
1370     retval = uloc_send_locations();
1371     if (retval != ZERR_NONE)
1372         return retval;
1373     retval = client_send_clients();
1374     if (retval != ZERR_NONE)
1375         return retval;
1376     retval = realm_send_realms();
1377     if (retval != ZERR_NONE)
1378         return retval;
1379     return send_done();
1380 }
1381
1382 /*
1383  * Send a sync indicating end of this host
1384  */
1385
1386 static Code_t
1387 send_done(void)
1388 {
1389     Code_t retval;
1390  
1391     zdbug((LOG_DEBUG, "send_done"));
1392
1393     retval = send_normal_tcp(SERVACK, bdump_sin.sin_port, ZEPHYR_ADMIN_CLASS,
1394                              "", ADMIN_DONE, myname, "", NULL, 0);
1395     return retval;
1396 }
1397
1398
1399 /*
1400  * Send a list off as the specified notice
1401  */
1402
1403 static Code_t
1404 send_list(ZNotice_Kind_t kind,
1405           int port,
1406           char *class_name,
1407           char *inst,
1408           char *opcode,
1409           char *sender,
1410           char *recip,
1411           char **lyst,
1412           int num)
1413 {
1414     ZNotice_t notice;
1415     char *pack;
1416     int packlen;
1417     Code_t retval;
1418  
1419     memset (&notice, 0, sizeof(notice));
1420
1421     notice.z_kind = kind;
1422     notice.z_port = port;
1423     notice.z_class = class_name;
1424     notice.z_class_inst = inst;
1425     notice.z_opcode = opcode;
1426     notice.z_sender = sender;
1427     notice.z_recipient = recip;
1428     notice.z_default_format = "";
1429     notice.z_num_other_fields = 0;
1430         
1431     retval = ZFormatNoticeList(&notice, lyst, num, &pack, &packlen, ZNOAUTH);
1432     if (retval != ZERR_NONE) {
1433         syslog(LOG_WARNING, "sl format: %s", error_message(retval));
1434         return retval;
1435     }
1436         
1437     retval = ZSendPacket(pack, packlen, 0);
1438     if (retval != ZERR_NONE)
1439         syslog(LOG_WARNING, "sl xmit: %s", error_message(retval));
1440     free(pack);
1441     return retval;
1442 }
1443
1444 /*
1445  * Send a message off as the specified notice, via TCP
1446  */
1447
1448 static Code_t
1449 send_normal_tcp(ZNotice_Kind_t kind,
1450                 int port,
1451                 char *class_name,
1452                 char *inst,
1453                 char *opcode,
1454                 char *sender,
1455                 char *recip,
1456                 char *message,
1457                 int len)
1458 {
1459     ZNotice_t notice;
1460     char *pack;
1461     int packlen, count;
1462     Code_t retval;
1463     u_short length;
1464  
1465     memset (&notice, 0, sizeof(notice));
1466
1467     notice.z_kind = kind;
1468     notice.z_port = port;
1469     notice.z_class = class_name;
1470     notice.z_class_inst = inst;
1471     notice.z_opcode = opcode;
1472     notice.z_sender = sender;
1473     notice.z_recipient = recip;
1474     notice.z_default_format = "";
1475     notice.z_message = message;
1476     notice.z_message_len = len;
1477     notice.z_num_other_fields = 0;
1478  
1479     retval = ZFormatNotice(&notice, &pack, &packlen, ZNOAUTH);
1480     if (retval != ZERR_NONE) {
1481         syslog(LOG_WARNING, "sn format: %s", error_message(retval));
1482         return retval;
1483     }
1484  
1485 #ifdef HAVE_KRB5
1486     if (bdump_ac) {
1487         krb5_data indata, outmsg;
1488         indata.length=packlen;
1489         indata.data=pack;
1490         memset(&outmsg, 0, sizeof(krb5_data));
1491         retval = krb5_mk_priv(Z_krb5_ctx, bdump_ac, &indata, &outmsg, NULL);
1492         if (retval != ZERR_NONE)
1493             return retval;
1494         if (outmsg.length > Z_MAXPKTLEN) {
1495             syslog(LOG_ERR, "sn: encrypted packet is too large");
1496             return ZERR_PKTLEN;
1497         }
1498         packlen = outmsg.length;
1499         free(pack);
1500         pack=malloc(packlen);
1501         if (!pack)
1502             return ENOMEM;
1503         memcpy(pack, outmsg.data, packlen);
1504         krb5_free_data_contents(Z_krb5_ctx, &outmsg);
1505     }
1506 #endif
1507
1508     length = htons((u_short) packlen);
1509  
1510     count = net_write(output, (char *) &length, sizeof(length));
1511     if (count != sizeof(length)) {
1512         if (count < 0) {
1513             syslog(LOG_WARNING, "snt xmit/len: %m");
1514             free(pack);
1515             return errno;
1516         } else {
1517             syslog(LOG_WARNING, "snt xmit: %d vs %d",sizeof(length),count);
1518             free(pack);
1519             return ZSRV_LEN;
1520         }
1521     }
1522     count = net_write(output, pack, packlen);
1523     if (count != packlen) {
1524         if (count < 0) {
1525             syslog(LOG_WARNING, "snt xmit: %m");
1526             free(pack);
1527             return errno;
1528         } else {
1529             syslog(LOG_WARNING, "snt xmit: %d vs %d",packlen, count);
1530             free(pack);
1531             return ZSRV_LEN;
1532         }
1533     }
1534     free(pack);
1535     return ZERR_NONE;
1536 }
1537
1538 /*
1539  * get a packet from the TCP socket
1540  * return 0 if successful, error code else
1541  */
1542
1543 static Code_t
1544 get_packet(void *packet, int len, int *retlen)
1545 {
1546     u_short length;
1547     int result;
1548  
1549     result = net_read(input, (char *) &length, sizeof(u_short));
1550     if (result < sizeof(short)) {
1551         if (result < 0) {
1552             return errno;
1553         } else {
1554             syslog(LOG_ERR, "get_pkt len: %d vs %d (%m)", result,
1555                    sizeof(short));
1556             return ZSRV_LEN;
1557         }
1558     }
1559         
1560     length = ntohs(length);
1561     if (len < length)
1562         return ZSRV_BUFSHORT;
1563     result = net_read(input, packet, (int) length);
1564     if (result < length) {
1565         if (result < 0) {
1566             return errno;
1567         } else {
1568             syslog(LOG_ERR, "get_pkt: %d vs %d (%m)", result, length);
1569             return ZSRV_LEN;
1570         }
1571     }
1572     *retlen = length;
1573     return ZERR_NONE;
1574 }
1575
1576 static Code_t
1577 extract_sin(ZNotice_t *notice, struct sockaddr_in *target)
1578 {
1579     char *cp = notice->z_message;
1580     char *buf;
1581
1582     buf = cp;
1583     if (!notice->z_message_len || *buf == '\0') {
1584         return ZSRV_PKSHORT;
1585     }
1586     target->sin_addr.s_addr = inet_addr(cp);
1587  
1588     cp += (strlen(cp) + 1);     /* past the null */
1589     if ((cp >= notice->z_message + notice->z_message_len) || (*cp == '\0')) {
1590         return(ZSRV_PKSHORT);
1591     }
1592     target->sin_port = htons((u_short) atoi(cp));
1593     target->sin_family = AF_INET;
1594     return ZERR_NONE;
1595 }
1596
1597 static int
1598 net_read(FILE *f, char *buf, int len)
1599 {
1600     int cc, len2 = 0;
1601  
1602     fflush (output);
1603     do {
1604         errno = 0;
1605         cc = fread(buf, 1, len, f);
1606         if (cc == 0)
1607           {
1608             if (feof(f))
1609               return len2;
1610             if (errno == 0)
1611               errno = EIO;
1612             return -1;
1613           }
1614         buf += cc;
1615         len2 += cc;
1616         len -= cc;
1617     } while (len > 0);
1618     return len2;
1619 }
1620
1621 static int
1622 net_write(FILE *f, char *buf, int len)
1623 {
1624     int cc;
1625     int wrlen = len;
1626     do {
1627         cc = fwrite (buf, 1, wrlen, f);
1628         if (cc == 0)
1629             return -1;
1630         buf += cc;
1631         wrlen -= cc;
1632     } while (wrlen > 0);
1633     return len;
1634 }
1635
1636 static int
1637 setup_file_pointers (void)
1638 {
1639     int fd;
1640
1641     input = fdopen (live_socket, "r");
1642     if (!input)
1643         return errno;
1644
1645     fd = dup (live_socket);
1646     if (fd < 0)
1647         return errno;
1648     output = fdopen (fd, "w");
1649     if (!output)
1650         return errno;
1651
1652     return 0;
1653 }
1654
1655 #ifdef HAVE_KRB5
1656 static int des_service_decrypt(unsigned char *in, unsigned char *out) {
1657 #ifndef HAVE_KRB4
1658     krb5_data dout;
1659     krb5_enc_data din;
1660
1661 #ifdef HAS_KRB5_C_DECRYPT
1662     dout.length = 8;
1663     dout.data = out;
1664
1665     din.ciphertext.length = 8;
1666     din.ciphertext.data = in;
1667     din.enctype = Z_enctype(server_key);
1668
1669 #ifdef HAVE_KRB5_CRYPTO_INIT
1670     return krb5_c_decrypt(Z_krb5_ctx, *server_key, 0, 0, &din, &dout);
1671 #else
1672     return krb5_c_decrypt(Z_krb5_ctx, server_key, 0, 0, &din, &dout);
1673 #endif
1674 #elif defined(HAVE_KRB5_CRYPTO_INIT)
1675     int ret;
1676     krb5_crypto crypto;
1677
1678     dout.length = 8;
1679     dout.data = out;
1680
1681     ret = krb5_crypto_init(Z_krb5_ctx, server_key, Z_enctype(server_key), &crypto);
1682     if (ret)
1683         return ret;
1684
1685     ret = krb5_decrypt_ivec(Z_krb5_ctx, crypto, 0, in, 8, &dout, NULL);
1686
1687     krb5_crypto_destroy(Z_krb5_ctx, crypto);
1688
1689     return ret;
1690 #endif
1691 #else
1692     des_ecb_encrypt((C_Block *)in, (C_Block *)out, serv_ksched.s, DES_DECRYPT);
1693 #endif
1694     return 0; /* sigh */
1695 }
1696 #endif