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