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