xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/kadm5/ipropd_slave.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: ipropd_slave.c,v 1.1.1.2 2014/04/24 12:45:48 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "iprop.h"
37 
38 __RCSID("NetBSD");
39 
40 static const char *config_name = "ipropd-slave";
41 
42 static krb5_log_facility *log_facility;
43 static char five_min[] = "5 min";
44 static char *server_time_lost = five_min;
45 static int time_before_lost;
46 const char *slave_str = NULL;
47 
48 static int
49 connect_to_master (krb5_context context, const char *master,
50 		   const char *port_str)
51 {
52     char port[NI_MAXSERV];
53     struct addrinfo *ai, *a;
54     struct addrinfo hints;
55     int error;
56     int s = -1;
57 
58     memset (&hints, 0, sizeof(hints));
59     hints.ai_socktype = SOCK_STREAM;
60 
61     if (port_str == NULL) {
62 	snprintf(port, sizeof(port), "%u", IPROP_PORT);
63 	port_str = port;
64     }
65 
66     error = getaddrinfo (master, port_str, &hints, &ai);
67     if (error) {
68 	krb5_warnx(context, "Failed to get address of to %s: %s",
69 		   master, gai_strerror(error));
70 	return -1;
71     }
72 
73     for (a = ai; a != NULL; a = a->ai_next) {
74 	char node[NI_MAXHOST];
75 	error = getnameinfo(a->ai_addr, a->ai_addrlen,
76 			    node, sizeof(node), NULL, 0, NI_NUMERICHOST);
77 	if (error)
78 	    strlcpy(node, "[unknown-addr]", sizeof(node));
79 
80 	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
81 	if (s < 0)
82 	    continue;
83 	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
84 	    krb5_warn(context, errno, "connection failed to %s[%s]",
85 		      master, node);
86 	    close (s);
87 	    continue;
88 	}
89 	krb5_warnx(context, "connection successful "
90 		   "to master: %s[%s]", master, node);
91 	break;
92     }
93     freeaddrinfo (ai);
94 
95     if (a == NULL)
96 	return -1;
97 
98     return s;
99 }
100 
101 static void
102 get_creds(krb5_context context, const char *keytab_str,
103 	  krb5_ccache *cache, const char *serverhost)
104 {
105     krb5_keytab keytab;
106     krb5_principal client;
107     krb5_error_code ret;
108     krb5_get_init_creds_opt *init_opts;
109     krb5_creds creds;
110     char *server;
111     char keytab_buf[256];
112 
113     if (keytab_str == NULL) {
114 	ret = krb5_kt_default_name (context, keytab_buf, sizeof(keytab_buf));
115 	if (ret)
116 	    krb5_err (context, 1, ret, "krb5_kt_default_name");
117 	keytab_str = keytab_buf;
118     }
119 
120     ret = krb5_kt_resolve(context, keytab_str, &keytab);
121     if(ret)
122 	krb5_err(context, 1, ret, "%s", keytab_str);
123 
124 
125     ret = krb5_sname_to_principal (context, slave_str, IPROP_NAME,
126 				   KRB5_NT_SRV_HST, &client);
127     if (ret) krb5_err(context, 1, ret, "krb5_sname_to_principal");
128 
129     ret = krb5_get_init_creds_opt_alloc(context, &init_opts);
130     if (ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
131 
132     asprintf (&server, "%s/%s", IPROP_NAME, serverhost);
133     if (server == NULL)
134 	krb5_errx (context, 1, "malloc: no memory");
135 
136     ret = krb5_get_init_creds_keytab(context, &creds, client, keytab,
137 				     0, server, init_opts);
138     free (server);
139     krb5_get_init_creds_opt_free(context, init_opts);
140     if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds");
141 
142     ret = krb5_kt_close(context, keytab);
143     if(ret) krb5_err(context, 1, ret, "krb5_kt_close");
144 
145     ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, cache);
146     if(ret) krb5_err(context, 1, ret, "krb5_cc_new_unique");
147 
148     ret = krb5_cc_initialize(context, *cache, client);
149     if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize");
150 
151     ret = krb5_cc_store_cred(context, *cache, &creds);
152     if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred");
153 
154     krb5_free_cred_contents(context, &creds);
155     krb5_free_principal(context, client);
156 }
157 
158 static krb5_error_code
159 ihave (krb5_context context, krb5_auth_context auth_context,
160        int fd, uint32_t version)
161 {
162     int ret;
163     u_char buf[8];
164     krb5_storage *sp;
165     krb5_data data;
166 
167     sp = krb5_storage_from_mem (buf, 8);
168     krb5_store_int32 (sp, I_HAVE);
169     krb5_store_int32 (sp, version);
170     krb5_storage_free (sp);
171     data.length = 8;
172     data.data   = buf;
173 
174     ret = krb5_write_priv_message(context, auth_context, &fd, &data);
175     if (ret)
176 	krb5_warn (context, ret, "krb5_write_message");
177     return ret;
178 }
179 
180 static void
181 receive_loop (krb5_context context,
182 	      krb5_storage *sp,
183 	      kadm5_server_context *server_context)
184 {
185     int ret;
186     off_t left, right;
187     void *buf;
188     int32_t vers, vers2;
189     ssize_t sret;
190 
191     /*
192      * Seek to the current version of the local database.
193      */
194     do {
195 	int32_t len, timestamp, tmp;
196 	enum kadm_ops op;
197 
198 	if(krb5_ret_int32 (sp, &vers) != 0)
199 	    return;
200 	krb5_ret_int32 (sp, &timestamp);
201 	krb5_ret_int32 (sp, &tmp);
202 	op = tmp;
203 	krb5_ret_int32 (sp, &len);
204 	if ((uint32_t)vers <= server_context->log_context.version)
205 	    krb5_storage_seek(sp, len + 8, SEEK_CUR);
206     } while((uint32_t)vers <= server_context->log_context.version);
207 
208     /*
209      * Read up rest of the entires into the memory...
210      */
211     left  = krb5_storage_seek (sp, -16, SEEK_CUR);
212     right = krb5_storage_seek (sp, 0, SEEK_END);
213     buf = malloc (right - left);
214     if (buf == NULL && (right - left) != 0)
215 	krb5_errx (context, 1, "malloc: no memory");
216 
217     /*
218      * ...and then write them out to the on-disk log.
219      */
220     krb5_storage_seek (sp, left, SEEK_SET);
221     krb5_storage_read (sp, buf, right - left);
222     sret = write (server_context->log_context.log_fd, buf, right-left);
223     if (sret != right - left)
224 	krb5_err(context, 1, errno, "Failed to write log to disk");
225     ret = fsync (server_context->log_context.log_fd);
226     if (ret)
227 	krb5_err(context, 1, errno, "Failed to sync log to disk");
228     free (buf);
229 
230     /*
231      * Go back to the startpoint and start to commit the entires to
232      * the database.
233      */
234     krb5_storage_seek (sp, left, SEEK_SET);
235 
236     for(;;) {
237 	int32_t len, len2, timestamp, tmp;
238 	off_t cur, cur2;
239 	enum kadm_ops op;
240 
241 	if(krb5_ret_int32 (sp, &vers) != 0)
242 	    break;
243 	ret = krb5_ret_int32 (sp, &timestamp);
244 	if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers);
245 	ret = krb5_ret_int32 (sp, &tmp);
246 	if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers);
247 	op = tmp;
248 	ret = krb5_ret_int32 (sp, &len);
249 	if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers);
250 	if (len < 0)
251 	    krb5_errx(context, 1, "log is corrupted, "
252 		      "negative length of entry version %ld: %ld",
253 		      (long)vers, (long)len);
254 	cur = krb5_storage_seek(sp, 0, SEEK_CUR);
255 
256 	krb5_warnx (context, "replaying entry %d", (int)vers);
257 
258 	ret = kadm5_log_replay (server_context,
259 				op, vers, len, sp);
260 	if (ret) {
261 	    const char *s = krb5_get_error_message(server_context->context, ret);
262 	    krb5_warnx (context,
263 			"kadm5_log_replay: %ld. Lost entry entry, "
264 			"Database out of sync ?: %s (%d)",
265 			(long)vers, s ? s : "unknown error", ret);
266 	    krb5_free_error_message(context, s);
267 	}
268 
269 	{
270 	    /*
271 	     * Make sure the krb5_log_replay does the right thing wrt
272 	     * reading out data from the sp.
273 	     */
274 	    cur2 = krb5_storage_seek(sp, 0, SEEK_CUR);
275 	    if (cur + len != cur2)
276 		krb5_errx(context, 1,
277 			  "kadm5_log_reply version: %ld didn't read the whole entry",
278 			  (long)vers);
279 	}
280 
281 	if (krb5_ret_int32 (sp, &len2) != 0)
282 	    krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers);
283 	if(krb5_ret_int32 (sp, &vers2) != 0)
284 	    krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers);
285 
286 	if (len != len2)
287 	    krb5_errx(context, 1, "entry %ld: len != len2", (long)vers);
288 	if (vers != vers2)
289 	    krb5_errx(context, 1, "entry %ld: vers != vers2", (long)vers);
290     }
291 
292     /*
293      * Update version
294      */
295 
296     server_context->log_context.version = vers;
297 }
298 
299 static void
300 receive (krb5_context context,
301 	 krb5_storage *sp,
302 	 kadm5_server_context *server_context)
303 {
304     int ret;
305 
306     ret = server_context->db->hdb_open(context,
307 				       server_context->db,
308 				       O_RDWR | O_CREAT, 0600);
309     if (ret)
310 	krb5_err (context, 1, ret, "db->open");
311 
312     receive_loop (context, sp, server_context);
313 
314     ret = server_context->db->hdb_close (context, server_context->db);
315     if (ret)
316 	krb5_err (context, 1, ret, "db->close");
317 }
318 
319 static void
320 send_im_here (krb5_context context, int fd,
321 	      krb5_auth_context auth_context)
322 {
323     krb5_storage *sp;
324     krb5_data data;
325     int ret;
326 
327     ret = krb5_data_alloc (&data, 4);
328     if (ret)
329 	krb5_err (context, 1, ret, "send_im_here");
330 
331     sp = krb5_storage_from_data (&data);
332     if (sp == NULL)
333 	krb5_errx (context, 1, "krb5_storage_from_data");
334     krb5_store_int32(sp, I_AM_HERE);
335     krb5_storage_free(sp);
336 
337     ret = krb5_write_priv_message(context, auth_context, &fd, &data);
338     krb5_data_free(&data);
339 
340     if (ret)
341 	krb5_err (context, 1, ret, "krb5_write_priv_message");
342 }
343 
344 static krb5_error_code
345 receive_everything (krb5_context context, int fd,
346 		    kadm5_server_context *server_context,
347 		    krb5_auth_context auth_context)
348 {
349     int ret;
350     krb5_data data;
351     int32_t vno = 0;
352     int32_t opcode;
353     krb5_storage *sp;
354 
355     char *dbname;
356     HDB *mydb;
357 
358     krb5_warnx(context, "receive complete database");
359 
360     asprintf(&dbname, "%s-NEW", server_context->db->hdb_name);
361     ret = hdb_create(context, &mydb, dbname);
362     if(ret)
363 	krb5_err(context,1, ret, "hdb_create");
364     free(dbname);
365 
366     ret = hdb_set_master_keyfile (context,
367 				  mydb, server_context->config.stash_file);
368     if(ret)
369 	krb5_err(context,1, ret, "hdb_set_master_keyfile");
370 
371     /* I really want to use O_EXCL here, but given that I can't easily clean
372        up on error, I won't */
373     ret = mydb->hdb_open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600);
374     if (ret)
375 	krb5_err (context, 1, ret, "db->open");
376 
377     sp = NULL;
378     do {
379 	ret = krb5_read_priv_message(context, auth_context, &fd, &data);
380 
381 	if (ret) {
382 	    krb5_warn (context, ret, "krb5_read_priv_message");
383 	    goto cleanup;
384 	}
385 
386 	sp = krb5_storage_from_data (&data);
387 	if (sp == NULL)
388 	    krb5_errx (context, 1, "krb5_storage_from_data");
389 	krb5_ret_int32 (sp, &opcode);
390 	if (opcode == ONE_PRINC) {
391 	    krb5_data fake_data;
392 	    hdb_entry_ex entry;
393 
394 	    krb5_storage_free(sp);
395 
396 	    fake_data.data   = (char *)data.data + 4;
397 	    fake_data.length = data.length - 4;
398 
399 	    memset(&entry, 0, sizeof(entry));
400 
401 	    ret = hdb_value2entry (context, &fake_data, &entry.entry);
402 	    if (ret)
403 		krb5_err (context, 1, ret, "hdb_value2entry");
404 	    ret = mydb->hdb_store(server_context->context,
405 				  mydb,
406 				  0, &entry);
407 	    if (ret)
408 		krb5_err (context, 1, ret, "hdb_store");
409 
410 	    hdb_free_entry (context, &entry);
411 	    krb5_data_free (&data);
412 	} else if (opcode == NOW_YOU_HAVE)
413 	    ;
414 	else
415 	    krb5_errx (context, 1, "strange opcode %d", opcode);
416     } while (opcode == ONE_PRINC);
417 
418     if (opcode != NOW_YOU_HAVE)
419 	krb5_errx (context, 1, "receive_everything: strange %d", opcode);
420 
421     krb5_ret_int32 (sp, &vno);
422     krb5_storage_free(sp);
423 
424     ret = kadm5_log_reinit (server_context);
425     if (ret)
426 	krb5_err(context, 1, ret, "kadm5_log_reinit");
427 
428     ret = kadm5_log_set_version (server_context, vno - 1);
429     if (ret)
430 	krb5_err (context, 1, ret, "kadm5_log_set_version");
431 
432     ret = kadm5_log_nop (server_context);
433     if (ret)
434 	krb5_err (context, 1, ret, "kadm5_log_nop");
435 
436     ret = mydb->hdb_rename (context, mydb, server_context->db->hdb_name);
437     if (ret)
438 	krb5_err (context, 1, ret, "db->rename");
439 
440  cleanup:
441     krb5_data_free (&data);
442 
443     ret = mydb->hdb_close (context, mydb);
444     if (ret)
445 	krb5_err (context, 1, ret, "db->close");
446 
447     ret = mydb->hdb_destroy (context, mydb);
448     if (ret)
449 	krb5_err (context, 1, ret, "db->destroy");
450 
451     krb5_warnx(context, "receive complete database, version %ld", (long)vno);
452     return ret;
453 }
454 
455 static char *config_file;
456 static char *realm;
457 static int version_flag;
458 static int help_flag;
459 static char *keytab_str;
460 static char *port_str;
461 #ifdef SUPPORT_DETACH
462 static int detach_from_console = 0;
463 #endif
464 
465 static struct getargs args[] = {
466     { "config-file", 'c', arg_string, &config_file, NULL, NULL },
467     { "realm", 'r', arg_string, &realm, NULL, NULL },
468     { "keytab", 'k', arg_string, &keytab_str,
469       "keytab to get authentication from", "kspec" },
470     { "time-lost", 0, arg_string, &server_time_lost,
471       "time before server is considered lost", "time" },
472     { "port", 0, arg_string, &port_str,
473       "port ipropd-slave will connect to", "port"},
474 #ifdef SUPPORT_DETACH
475     { "detach", 0, arg_flag, &detach_from_console,
476       "detach from console", NULL },
477 #endif
478     { "hostname", 0, arg_string, rk_UNCONST(&slave_str),
479       "hostname of slave (if not same as hostname)", "hostname" },
480     { "version", 0, arg_flag, &version_flag, NULL, NULL },
481     { "help", 0, arg_flag, &help_flag, NULL, NULL }
482 };
483 
484 static int num_args = sizeof(args) / sizeof(args[0]);
485 
486 static void
487 usage(int status)
488 {
489     arg_printusage(args, num_args, NULL, "master");
490     exit(status);
491 }
492 
493 int
494 main(int argc, char **argv)
495 {
496     krb5_error_code ret;
497     krb5_context context;
498     krb5_auth_context auth_context;
499     void *kadm_handle;
500     kadm5_server_context *server_context;
501     kadm5_config_params conf;
502     int master_fd;
503     krb5_ccache ccache;
504     krb5_principal server;
505     char **files;
506     int optidx = 0;
507     time_t reconnect_min;
508     time_t backoff;
509     time_t reconnect_max;
510     time_t reconnect;
511     time_t before = 0;
512 
513     const char *master;
514 
515     setprogname(argv[0]);
516 
517     if(getarg(args, num_args, argc, argv, &optidx))
518 	usage(1);
519 
520     if(help_flag)
521 	usage(0);
522     if(version_flag) {
523 	print_version(NULL);
524 	exit(0);
525     }
526 
527     ret = krb5_init_context(&context);
528     if (ret)
529 	errx (1, "krb5_init_context failed: %d", ret);
530 
531     setup_signal();
532 
533     if (config_file == NULL) {
534 	if (asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)) == -1
535 	    || config_file == NULL)
536 	    errx(1, "out of memory");
537     }
538 
539     ret = krb5_prepend_config_files_default(config_file, &files);
540     if (ret)
541 	krb5_err(context, 1, ret, "getting configuration files");
542 
543     ret = krb5_set_config_files(context, files);
544     krb5_free_config_files(files);
545     if (ret)
546 	krb5_err(context, 1, ret, "reading configuration files");
547 
548     argc -= optidx;
549     argv += optidx;
550 
551     if (argc != 1)
552 	usage(1);
553 
554     master = argv[0];
555 
556 #ifdef SUPPORT_DETACH
557     if (detach_from_console)
558 	daemon(0, 0);
559 #endif
560     pidfile (NULL);
561     krb5_openlog (context, "ipropd-slave", &log_facility);
562     krb5_set_warn_dest(context, log_facility);
563 
564     ret = krb5_kt_register(context, &hdb_kt_ops);
565     if(ret)
566 	krb5_err(context, 1, ret, "krb5_kt_register");
567 
568     time_before_lost = parse_time (server_time_lost,  "s");
569     if (time_before_lost < 0)
570 	krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost);
571 
572     memset(&conf, 0, sizeof(conf));
573     if(realm) {
574 	conf.mask |= KADM5_CONFIG_REALM;
575 	conf.realm = realm;
576     }
577     ret = kadm5_init_with_password_ctx (context,
578 					KADM5_ADMIN_SERVICE,
579 					NULL,
580 					KADM5_ADMIN_SERVICE,
581 					&conf, 0, 0,
582 					&kadm_handle);
583     if (ret)
584 	krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
585 
586     server_context = (kadm5_server_context *)kadm_handle;
587 
588     ret = kadm5_log_init (server_context);
589     if (ret)
590 	krb5_err (context, 1, ret, "kadm5_log_init");
591 
592     get_creds(context, keytab_str, &ccache, master);
593 
594     ret = krb5_sname_to_principal (context, master, IPROP_NAME,
595 				   KRB5_NT_SRV_HST, &server);
596     if (ret)
597 	krb5_err (context, 1, ret, "krb5_sname_to_principal");
598 
599     auth_context = NULL;
600     master_fd = -1;
601 
602     krb5_appdefault_time(context, config_name, NULL, "reconnect-min",
603 			 10, &reconnect_min);
604     krb5_appdefault_time(context, config_name, NULL, "reconnect-max",
605 			 300, &reconnect_max);
606     krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff",
607 			 10, &backoff);
608     reconnect = reconnect_min;
609 
610     while (!exit_flag) {
611 	time_t now, elapsed;
612 	int connected = FALSE;
613 
614 	now = time(NULL);
615 	elapsed = now - before;
616 
617 	if (elapsed < reconnect) {
618 	    time_t left = reconnect - elapsed;
619 	    krb5_warnx(context, "sleeping %d seconds before "
620 		       "retrying to connect", (int)left);
621 	    sleep(left);
622 	}
623 	before = now;
624 
625 	master_fd = connect_to_master (context, master, port_str);
626 	if (master_fd < 0)
627 	    goto retry;
628 
629 	reconnect = reconnect_min;
630 
631 	if (auth_context) {
632 	    krb5_auth_con_free(context, auth_context);
633 	    auth_context = NULL;
634 	    krb5_cc_destroy(context, ccache);
635 	    get_creds(context, keytab_str, &ccache, master);
636 	}
637 	ret = krb5_sendauth (context, &auth_context, &master_fd,
638 			     IPROP_VERSION, NULL, server,
639 			     AP_OPTS_MUTUAL_REQUIRED, NULL, NULL,
640 			     ccache, NULL, NULL, NULL);
641 	if (ret) {
642 	    krb5_warn (context, ret, "krb5_sendauth");
643 	    goto retry;
644 	}
645 
646 	krb5_warnx(context, "ipropd-slave started at version: %ld",
647 		   (long)server_context->log_context.version);
648 
649 	ret = ihave (context, auth_context, master_fd,
650 		     server_context->log_context.version);
651 	if (ret)
652 	    goto retry;
653 
654 	connected = TRUE;
655 
656 	while (connected && !exit_flag) {
657 	    krb5_data out;
658 	    krb5_storage *sp;
659 	    int32_t tmp;
660 	    fd_set readset;
661 	    struct timeval to;
662 
663 #ifndef NO_LIMIT_FD_SETSIZE
664 	    if (master_fd >= FD_SETSIZE)
665 		krb5_errx (context, 1, "fd too large");
666 #endif
667 
668 	    FD_ZERO(&readset);
669 	    FD_SET(master_fd, &readset);
670 
671 	    to.tv_sec = time_before_lost;
672 	    to.tv_usec = 0;
673 
674 	    ret = select (master_fd + 1,
675 			  &readset, NULL, NULL, &to);
676 	    if (ret < 0) {
677 		if (errno == EINTR)
678 		    continue;
679 		else
680 		    krb5_err (context, 1, errno, "select");
681 	    }
682 	    if (ret == 0)
683 		krb5_errx (context, 1, "server didn't send a message "
684 			   "in %d seconds", time_before_lost);
685 
686 	    ret = krb5_read_priv_message(context, auth_context, &master_fd, &out);
687 	    if (ret) {
688 		krb5_warn (context, ret, "krb5_read_priv_message");
689 		connected = FALSE;
690 		continue;
691 	    }
692 
693 	    sp = krb5_storage_from_mem (out.data, out.length);
694 	    krb5_ret_int32 (sp, &tmp);
695 	    switch (tmp) {
696 	    case FOR_YOU :
697 		receive (context, sp, server_context);
698 		ret = ihave (context, auth_context, master_fd,
699 			     server_context->log_context.version);
700 		if (ret)
701 		    connected = FALSE;
702 		break;
703 	    case TELL_YOU_EVERYTHING :
704 		ret = receive_everything (context, master_fd, server_context,
705 					  auth_context);
706 		if (ret)
707 		    connected = FALSE;
708 		break;
709 	    case ARE_YOU_THERE :
710 		send_im_here (context, master_fd, auth_context);
711 		break;
712 	    case NOW_YOU_HAVE :
713 	    case I_HAVE :
714 	    case ONE_PRINC :
715 	    case I_AM_HERE :
716 	    default :
717 		krb5_warnx (context, "Ignoring command %d", tmp);
718 		break;
719 	    }
720 	    krb5_storage_free (sp);
721 	    krb5_data_free (&out);
722 
723 	}
724     retry:
725 	if (connected == FALSE)
726 	    krb5_warnx (context, "disconnected for server");
727 	if (exit_flag)
728 	    krb5_warnx (context, "got an exit signal");
729 
730 	if (master_fd >= 0)
731 	    close(master_fd);
732 
733 	reconnect += backoff;
734 	if (reconnect > reconnect_max)
735 	    reconnect = reconnect_max;
736     }
737 
738     if (0);
739 #ifndef NO_SIGXCPU
740     else if(exit_flag == SIGXCPU)
741 	krb5_warnx(context, "%s CPU time limit exceeded", getprogname());
742 #endif
743     else if(exit_flag == SIGINT || exit_flag == SIGTERM)
744 	krb5_warnx(context, "%s terminated", getprogname());
745     else
746 	krb5_warnx(context, "%s unexpected exit reason: %ld",
747 		       getprogname(), (long)exit_flag);
748 
749     return 0;
750 }
751