xref: /illumos-gate/usr/src/cmd/krb5/slave/kprop.c (revision 1a5e258f5471356ca102c7176637cdce45bac147)
1 /*
2  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * slave/kprop.c
7  *
8  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
9  * All Rights Reserved.
10  *
11  * Export of this software from the United States of America may
12  *   require a specific license from the United States Government.
13  *   It is the responsibility of any person or organization contemplating
14  *   export to obtain such a license before exporting.
15  *
16  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17  * distribute this software and its documentation for any purpose and
18  * without fee is hereby granted, provided that the above copyright
19  * notice appear in all copies and that both that copyright notice and
20  * this permission notice appear in supporting documentation, and that
21  * the name of M.I.T. not be used in advertising or publicity pertaining
22  * to distribution of the software without specific, written prior
23  * permission.  Furthermore if you modify this software you must label
24  * your software as modified software and not distribute it in such a
25  * fashion that it might be confused with the original M.I.T. software.
26  * M.I.T. makes no representations about the suitability of
27  * this software for any purpose.  It is provided "as is" without express
28  * or implied warranty.
29  *
30  *
31  */
32 
33 
34 #include <errno.h>
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <sys/file.h>
38 #include <signal.h>
39 #include <string.h>
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <sys/stat.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <sys/param.h>
46 #include <netdb.h>
47 #include <fcntl.h>
48 #include <libintl.h>
49 #include <locale.h>
50 #include <k5-int.h>
51 #include "com_err.h"
52 #include "kprop.h"
53 static char *kprop_version = KPROP_PROT_VERSION;
54 
55 char	*progname = 0;
56 int     debug = 0;
57 char	*srvtab = 0;
58 char	*slave_host;
59 char	*realm = 0;
60 char	*file = KPROP_DEFAULT_FILE;
61 short	port = 0;
62 
63 krb5_principal	my_principal;		/* The Kerberos principal we'll be */
64 				/* running under, initialized in */
65 				/* get_tickets() */
66 krb5_ccache	ccache;		/* Credentials cache which we'll be using */
67 krb5_creds	creds;
68 krb5_address	sender_addr;
69 krb5_address	receiver_addr;
70 
71 void	PRS
72 	(int, char **);
73 void	get_tickets
74 	(krb5_context);
75 static void usage
76 	(void);
77 krb5_error_code open_connection
78 	(char *, int *, char *, unsigned int);
79 void	kerberos_authenticate
80 	(krb5_context, krb5_auth_context *,
81 		   int, krb5_principal, krb5_creds **);
82 int	open_database
83 	(krb5_context, char *, int *);
84 void	close_database
85 	(krb5_context, int);
86 void	xmit_database
87 	(krb5_context, krb5_auth_context, krb5_creds *,
88 		   int, int, int);
89 void	send_error
90 	(krb5_context, krb5_creds *, int, char *, krb5_error_code);
91 void	update_last_prop_file
92 	(char *, char *);
93 
94 static void usage()
95 {
96 	fprintf(stderr,
97 		gettext
98 		("\nUsage: %s [-r realm] [-f file] [-d] [-P port] [-s srvtab] slave_host\n\n"),
99 		progname);
100 	exit(1);
101 }
102 
103 int
104 main(argc, argv)
105 	int	argc;
106 	char	**argv;
107 {
108 	int	fd, database_fd, database_size;
109 	krb5_error_code	retval;
110 	krb5_context context;
111 	krb5_creds *my_creds;
112 	krb5_auth_context auth_context;
113 #define	ERRMSGSIZ	256
114 	char	Errmsg[ERRMSGSIZ];
115 
116 	(void) setlocale(LC_ALL, "");
117 
118 #if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
119 #define	TEXT_DOMAIN	"KPROP_TEST"	/* Use this only if it weren't */
120 #endif
121 
122 	(void) textdomain(TEXT_DOMAIN);
123 
124 	retval = krb5_init_context(&context);
125 	if (retval) {
126 		com_err(argv[0], retval, gettext("while initializing krb5"));
127 		exit(1);
128 	}
129 	PRS(argc, argv);
130 	get_tickets(context);
131 
132 	database_fd = open_database(context, file, &database_size);
133 	retval = open_connection(slave_host, &fd, Errmsg, sizeof(Errmsg));
134 	if (retval) {
135 		com_err(progname, retval, gettext("%s while opening connection to %s"),
136 			Errmsg, slave_host);
137 		exit(1);
138 	}
139 	if (fd < 0) {
140 		fprintf(stderr,
141 			gettext("%s: %s while opening connection to %s\n"),
142 			progname, Errmsg, slave_host);
143 		exit(1);
144 	}
145 	kerberos_authenticate(context, &auth_context, fd, my_principal,
146 			      &my_creds);
147 	xmit_database(context, auth_context, my_creds, fd, database_fd,
148 		      database_size);
149 	update_last_prop_file(slave_host, file);
150 	printf(gettext("Database propagation to %s: SUCCEEDED\n"), slave_host);
151 	krb5_free_cred_contents(context, my_creds);
152 	close_database(context, database_fd);
153 	exit(0);
154 }
155 
156 void PRS(argc, argv)
157 	int	argc;
158 	char	**argv;
159 {
160 	int c;
161 	register char	*word, ch;
162 	extern int optind;
163 	extern char *optarg;
164 
165 	progname = argv[0];
166 	while ((c= getopt(argc, argv, "r:f:dP:s:h:")) != EOF) {
167 		switch (c) {
168 				case 'r':
169 			realm = optarg;
170 					if (!realm)
171 						usage();
172 					break;
173 				case 'f':
174 			file = optarg;
175 					if (!file)
176 						usage();
177 					break;
178 				case 'd':
179 					debug++;
180 					break;
181 				case 'P':
182 			port = atoi(optarg);
183 					if (!port)
184 						usage();
185 					break;
186 				case 's':
187 			srvtab = optarg;
188 					if (!srvtab)
189 						usage();
190 					break;
191 		case '?':
192 				default:
193 			printf (gettext("Error \n"));
194 					usage();
195 				}
196 			}
197 	argc -= optind;
198 	argv = &argv[optind];
199 	if (*argv)
200 		slave_host = *argv;
201 			else
202 		usage();
203 }
204 
205 void get_tickets(context)
206     krb5_context context;
207 {
208 	char   buf[BUFSIZ];
209 	krb5_error_code retval;
210 	static char tkstring[] = "/tmp/kproptktXXXXXX";
211 	krb5_keytab keytab = NULL;
212 	krb5_get_init_creds_opt opt;
213 	char *svcname = NULL;
214 	char *def_realm = NULL;
215 	char *master_host = NULL;
216 
217 
218 	/*
219 	 * Figure out what tickets we'll be using to send stuff
220 	 */
221 	if (realm) {
222 	    if ((def_realm = strdup(realm)) == NULL) {
223 	      com_err(progname, ENOMEM,
224 		      gettext("while allocating default realm name '%s'"),
225 		      realm);
226 	      exit(1);
227 	    }
228 	} else {
229 	    retval = krb5_get_default_realm(context, &def_realm);
230 	    if (retval) {
231 	        com_err(progname, retval,
232 			gettext("while getting default realm"));
233 	        exit(1);
234 	    }
235 	}
236 
237 	/*
238 	 * Always pick up the master hostname from krb5.conf, as
239 	 * opposed to picking up the localhost, so we do not get bit
240 	 * if the master KDC is HA and hence points to a logicalhost.
241 	 */
242 	retval = kadm5_get_master(context, def_realm, &master_host);
243 	if (retval) {
244 	    free(def_realm);
245 	    com_err(progname, retval,
246 		gettext("while getting admin server fqdn"));
247 	    exit(1);
248 	}
249 
250 	retval = krb5_sname_to_principal(context, master_host, NULL,
251 					 KRB5_NT_SRV_HST, &my_principal);
252 
253 	free(def_realm);
254 	free(master_host);
255 	if (retval) {
256 	    com_err(progname, errno, gettext("while setting client principal name"));
257 	    exit(1);
258 	}
259 	if (realm) {
260 	    retval = krb5_set_principal_realm(context, my_principal, realm);
261 	    if (retval) {
262 	        com_err(progname, errno,
263 			 gettext("while setting client principal realm"));
264 		exit(1);
265 	    }
266 	}
267 #if 0
268 	krb5_princ_type(context, my_principal) = KRB5_NT_PRINCIPAL;
269 #endif
270 
271 	/*
272 	 * Initialize cache file which we're going to be using
273 	 */
274 	(void) mktemp(tkstring);
275 	snprintf(buf, sizeof (buf), "FILE:%s", tkstring);
276 
277 	retval = krb5_cc_resolve(context, buf, &ccache);
278 	if (retval) {
279 		com_err(progname, retval, gettext("while opening credential cache %s"),
280 			buf);
281 		exit(1);
282 	}
283 
284 	retval = krb5_cc_initialize(context, ccache, my_principal);
285 	if (retval) {
286 		com_err (progname, retval, gettext("when initializing cache %s"),
287 			 buf);
288 		exit(1);
289 	}
290 
291 	/*
292 	 * Get the tickets we'll need.
293 	 *
294 	 * Construct the principal name for the slave host.
295 	 */
296 	memset((char *)&creds, 0, sizeof(creds));
297 	retval = krb5_sname_to_principal(context,
298 					 slave_host, KPROP_SERVICE_NAME,
299 					 KRB5_NT_SRV_HST, &creds.server);
300 	if (retval) {
301 	    com_err(progname, errno, gettext("while setting server principal name"));
302 	    (void) krb5_cc_destroy(context, ccache);
303 	    exit(1);
304 	}
305 	if (realm) {
306 	    retval = krb5_set_principal_realm(context, creds.server, realm);
307 	    if (retval) {
308 	        com_err(progname, errno,
309 			gettext("while setting server principal realm"));
310 		exit(1);
311 	    }
312 	}
313 
314 	/*
315 	 * Now fill in the client....
316 	 */
317 	retval = krb5_copy_principal(context, my_principal, &creds.client);
318 	if (retval) {
319 		com_err(progname, retval, gettext("While copying client principal"));
320 		(void) krb5_cc_destroy(context, ccache);
321 		exit(1);
322 	}
323 	if (srvtab) {
324 	        retval = krb5_kt_resolve(context, srvtab, &keytab);
325 		if (retval) {
326 			com_err(progname, retval, gettext("while resolving keytab"));
327 			(void) krb5_cc_destroy(context, ccache);
328 			exit(1);
329 		}
330 	}
331 	(void) memset(&opt, 0, sizeof (opt));
332 	krb5_get_init_creds_opt_init(&opt);
333 	retval = krb5_unparse_name(context,  creds.server, &svcname);
334 	if (retval) {
335 		com_err(progname, errno, gettext("while parsing svc principal name"));
336 		(void) krb5_cc_destroy(context, ccache);
337 		exit (1);
338 	}
339 	retval = krb5_get_init_creds_keytab(context, &creds, creds.client,
340 				keytab,  0, svcname, &opt);
341 
342 	if (svcname)
343 		free(svcname);
344 
345 	if (retval) {
346 		com_err(progname, retval, gettext("while getting initial ticket\n"));
347 		(void) krb5_cc_destroy(context, ccache);
348 		exit(1);
349 	}
350 
351 	if (keytab)
352 	    (void) krb5_kt_close(context, keytab);
353 
354 	/*
355 	 * Now destroy the cache right away --- the credentials we
356 	 * need will be in my_creds.
357 	 */
358 	retval = krb5_cc_destroy(context, ccache);
359 	if (retval) {
360 		com_err(progname, retval, gettext("while destroying ticket cache"));
361 		exit(1);
362 	}
363 }
364 
365 /* SUNW14resync - SOCKET is defed in 1.4 in port-sockets.h */
366 #ifdef SOCKET
367 #undef SOCKET
368 #endif
369 
370 krb5_error_code
371 open_connection(host, fd, Errmsg, ErrmsgSz)
372 	char		*host;
373 	int		*fd;
374 	char		*Errmsg;
375 	unsigned int	 ErrmsgSz;
376 {
377 	int	s;
378 	krb5_error_code	retval;
379 
380 	int	socket_length;
381 	struct addrinfo hints, *ai, *aitop;
382 	struct sockaddr_storage	  ss;
383 	char serv_or_port[NI_MAXSERV];
384 	enum err_types {SOCKET, CONNECT};
385 	int which_err;
386 
387 	memset(&hints, 0, sizeof(hints));
388 	hints.ai_family = AF_UNSPEC;    /* go for either IPv4 or v6 */
389 	hints.ai_socktype = SOCK_STREAM;
390 
391 	if (port != 0)
392 		(void) snprintf(serv_or_port, sizeof(serv_or_port), ("%hu"),
393 				port);
394 	else
395 		strncpy(serv_or_port, KPROP_SERVICE, sizeof(serv_or_port));
396 
397 	if (getaddrinfo(host, serv_or_port, &hints, &aitop) != 0) {
398 		(void) snprintf(Errmsg, ERRMSGSIZ, gettext("%s: unknown host"),
399 				host);
400 		*fd = -1;
401 		return(0);
402 	}
403 
404 	for (ai = aitop; ai; ai = ai->ai_next) {
405 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
406 			continue;
407 
408 		s = socket(ai->ai_family, SOCK_STREAM, 0);
409 		if (s < 0) {
410 			which_err = SOCKET;
411 			retval = errno;
412 			continue;
413 		}
414 
415 		if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 &&
416 		    errno != EINPROGRESS) {
417 			which_err = CONNECT;
418 			retval = errno;
419 			close(s);
420 			continue;	/* fail -- try next */
421 		}
422 
423 		break; /* success */
424 	}
425 
426 	if (ai == NULL) {
427 		switch (which_err) {
428 			case SOCKET:
429 				(void) snprintf(Errmsg, ERRMSGSIZ,
430 						gettext("in call to socket"));
431 				break;
432 			case CONNECT:
433 				(void) snprintf(Errmsg, ERRMSGSIZ,
434 						gettext("in call to connect"));
435 				break;
436 			default :
437 				retval = -1; /* generic error */
438 				(void) snprintf(Errmsg, ERRMSGSIZ,
439 					gettext("could not setup network"));
440 				break;
441 		}
442 		if (aitop != NULL)
443 			freeaddrinfo(aitop);
444 		return(retval);
445 	}
446 	*fd = s;
447 
448 	/*
449 	 * Set receiver_addr and sender_addr.
450 	 */
451 	if (cvtkaddr((struct sockaddr_storage *)ai->ai_addr, &receiver_addr)
452 			== NULL) {
453 		retval = errno;
454 		com_err(progname, errno,
455 			gettext("while converting socket address"));
456 		if (aitop != NULL)
457 			freeaddrinfo(aitop);
458 		return(retval);
459 	}
460 	if (aitop != NULL)
461 		freeaddrinfo(aitop);
462 
463 	socket_length = sizeof(ss);
464 	if (getsockname(s, (struct sockaddr *)&ss, &socket_length) < 0) {
465 		retval = errno;
466 		close(s);
467 		(void) snprintf(Errmsg, ERRMSGSIZ,
468 				gettext("in call to getsockname"));
469 		return(retval);
470 	}
471 
472 	if (cvtkaddr(&ss, &sender_addr) == NULL) {
473 		retval = errno;
474 		com_err(progname, errno,
475 			gettext("while converting socket address"));
476 		return(retval);
477 	}
478 
479 	return(0);
480 }
481 
482 
483 void kerberos_authenticate(context, auth_context, fd, me, new_creds)
484     krb5_context context;
485     krb5_auth_context *auth_context;
486     int	fd;
487     krb5_principal me;
488     krb5_creds ** new_creds;
489 {
490 	krb5_error_code	retval;
491 	krb5_error	*error = NULL;
492 	krb5_ap_rep_enc_part	*rep_result;
493 
494     retval = krb5_auth_con_init(context, auth_context);
495     if (retval)
496 	exit(1);
497 
498     krb5_auth_con_setflags(context, *auth_context,
499 			   KRB5_AUTH_CONTEXT_DO_SEQUENCE);
500 
501     retval = krb5_auth_con_setaddrs(context, *auth_context, &sender_addr,
502 				    &receiver_addr);
503     if (retval) {
504 	com_err(progname, retval, gettext("in krb5_auth_con_setaddrs"));
505 	exit(1);
506     }
507 
508     retval = krb5_sendauth(context, auth_context, (void *)&fd,
509 			   kprop_version, me, creds.server,
510 			   AP_OPTS_MUTUAL_REQUIRED, NULL, &creds, NULL,
511 			   &error, &rep_result, new_creds);
512     if (retval) {
513         com_err(progname, retval, gettext("while authenticating to server"));
514 	if (error) {
515 	    if (error->error == KRB_ERR_GENERIC) {
516 	        if (error->text.data)
517 		    fprintf(stderr,
518 			    gettext("Generic remote error: %s\n"),
519 			    error->text.data);
520 	    } else if (error->error) {
521 	        com_err(progname,
522 			(krb5_error_code) error->error + ERROR_TABLE_BASE_krb5,
523 		gettext("signalled from server"));
524 		if (error->text.data)
525 		    fprintf(stderr,
526 			    gettext("Error text from server: %s\n"),
527 			    error->text.data);
528 	    }
529 	    krb5_free_error(context, error);
530 	}
531 	exit(1);
532     }
533     krb5_free_ap_rep_enc_part(context, rep_result);
534 }
535 
536 char * dbpathname;
537 /*
538  * Open the Kerberos database dump file.  Takes care of locking it
539  * and making sure that the .ok file is more recent that the database
540  * dump file itself.
541  *
542  * Returns the file descriptor of the database dump file.  Also fills
543  * in the size of the database file.
544  */
545 int
546 open_database(context, data_fn, size)
547     krb5_context context;
548     char *data_fn;
549     int	*size;
550 {
551 	int		fd;
552 	int		err;
553 	struct stat 	stbuf, stbuf_ok;
554 	char		*data_ok_fn;
555 	static char ok[] = ".dump_ok";
556 
557 	dbpathname = strdup(data_fn);
558 	if (!dbpathname) {
559  	    com_err(progname, ENOMEM, gettext("allocating database file name '%s'"),
560  		    data_fn);
561  	    exit(1);
562  	}
563 	if ((fd = open(dbpathname, O_RDONLY)) < 0) {
564 		com_err(progname, errno, gettext("while trying to open %s"),
565 			dbpathname);
566 		exit(1);
567 	}
568 
569 	err = krb5_lock_file(context, fd,
570 			     KRB5_LOCKMODE_SHARED|KRB5_LOCKMODE_DONTBLOCK);
571 	if (err == EAGAIN || err == EWOULDBLOCK || errno == EACCES) {
572 	    com_err(progname, 0, gettext("database locked"));
573 	    exit(1);
574 	} else if (err) {
575 	    com_err(progname, err, gettext("while trying to lock '%s'"), dbpathname);
576 	    exit(1);
577 	}
578 	if (fstat(fd, &stbuf)) {
579 		com_err(progname, errno, gettext("while trying to stat %s"),
580 			data_fn);
581 		exit(1);
582 	}
583 	if ((data_ok_fn = (char *) malloc(strlen(data_fn)+strlen(ok)+1))
584 	    == NULL) {
585 		com_err(progname, ENOMEM, gettext("while trying to malloc data_ok_fn"));
586 		exit(1);
587 	}
588 	strcpy(data_ok_fn, data_fn);
589 	strcat(data_ok_fn, ok);
590 	if (stat(data_ok_fn, &stbuf_ok)) {
591 		com_err(progname, errno, gettext("while trying to stat %s"),
592 			data_ok_fn);
593 		free(data_ok_fn);
594 		exit(1);
595 	}
596 	free(data_ok_fn);
597 	if (stbuf.st_mtime > stbuf_ok.st_mtime) {
598 		com_err(progname, 0, gettext("'%s' more recent than '%s'."),
599 			data_fn, data_ok_fn);
600 		exit(1);
601 	}
602 	*size = stbuf.st_size;
603 	return(fd);
604 }
605 
606 void
607 close_database(context, fd)
608     krb5_context context;
609     int fd;
610 {
611     int err;
612     err = krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK);
613     if (err)
614 	com_err(progname, err, gettext("while unlocking database '%s'"), dbpathname);
615     free(dbpathname);
616     (void)close(fd);
617     return;
618 }
619 
620 /*
621  * Now we send over the database.  We use the following protocol:
622  * Send over a KRB_SAFE message with the size.  Then we send over the
623  * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV.
624  * Then we expect to see a KRB_SAFE message with the size sent back.
625  *
626  * At any point in the protocol, we may send a KRB_ERROR message; this
627  * will abort the entire operation.
628  */
629 void
630 xmit_database(context, auth_context, my_creds, fd, database_fd,
631 	      in_database_size)
632     krb5_context context;
633     krb5_auth_context auth_context;
634     krb5_creds *my_creds;
635     int	fd;
636     int	database_fd;
637     int	in_database_size;
638 {
639 	krb5_int32	sent_size, n;
640 	krb5_data	inbuf, outbuf;
641 	char		buf[KPROP_BUFSIZ];
642 	krb5_error_code	retval;
643 	krb5_error	*error;
644 	/* These must be 4 bytes */
645 	krb5_ui_4	database_size = in_database_size;
646 	krb5_ui_4	send_size;
647 
648 	/*
649 	 * Send over the size
650 	 */
651 	send_size = htonl(database_size);
652 	inbuf.data = (char *) &send_size;
653 	inbuf.length = sizeof(send_size); /* must be 4, really */
654 	/* KPROP_CKSUMTYPE */
655 	retval = krb5_mk_safe(context, auth_context, &inbuf,
656 			      &outbuf, NULL);
657 	if (retval) {
658 		com_err(progname, retval, gettext("while encoding database size"));
659 		send_error(context, my_creds, fd, gettext("while encoding database size"), retval);
660 		exit(1);
661 	}
662 
663 	retval = krb5_write_message(context, (void *) &fd, &outbuf);
664 	if (retval) {
665 		krb5_free_data_contents(context, &outbuf);
666 		com_err(progname, retval, gettext("while sending database size"));
667 		exit(1);
668 	}
669 	krb5_free_data_contents(context, &outbuf);
670 	/*
671 	 * Initialize the initial vector.
672 	 */
673 	retval = krb5_auth_con_initivector(context, auth_context);
674 	if (retval) {
675 	    send_error(context, my_creds, fd,
676 		   gettext("failed while initializing i_vector"), retval);
677 	    com_err(progname, retval, gettext("while allocating i_vector"));
678 	    exit(1);
679 	}
680 
681 	/*
682 	 * Send over the file, block by block....
683 	 */
684 	inbuf.data = buf;
685 	sent_size = 0;
686 	while ((n = read(database_fd, buf, sizeof(buf)))) {
687 		inbuf.length = n;
688 		retval = krb5_mk_priv(context, auth_context, &inbuf,
689 				      &outbuf, NULL);
690 		if (retval) {
691 			snprintf(buf, sizeof (buf),
692 				gettext("while encoding database block starting at %d"),
693 				sent_size);
694 			com_err(progname, retval, buf);
695 			send_error(context, my_creds, fd, buf, retval);
696 			exit(1);
697 		}
698 
699 		retval = krb5_write_message(context, (void *)&fd,&outbuf);
700 		if (retval) {
701 			krb5_free_data_contents(context, &outbuf);
702 			com_err(progname, retval,
703 				gettext("while sending database block starting at %d"),
704 				sent_size);
705 			exit(1);
706 		}
707 		krb5_free_data_contents(context, &outbuf);
708 		sent_size += n;
709 		if (debug)
710 			printf(gettext("%d bytes sent.\n"), sent_size);
711 	}
712 	if (sent_size != database_size) {
713 		com_err(progname, 0, gettext("Premature EOF found for database file!"));
714 		send_error(context, my_creds, fd,gettext("Premature EOF found for database file!"),
715 			   KRB5KRB_ERR_GENERIC);
716 		exit(1);
717 	}
718 
719 	/*
720 	 * OK, we've sent the database; now let's wait for a success
721 	 * indication from the remote end.
722 	 */
723 	retval = krb5_read_message(context, (void *) &fd, &inbuf);
724 	if (retval) {
725 		com_err(progname, retval,
726 			gettext("while reading response from server"));
727 		exit(1);
728 	}
729 	/*
730 	 * If we got an error response back from the server, display
731 	 * the error message
732 	 */
733 	if (krb5_is_krb_error(&inbuf)) {
734  	        retval = krb5_rd_error(context, &inbuf, &error);
735 		if (retval) {
736 			com_err(progname, retval,
737 				gettext("while decoding error response from server"));
738 			exit(1);
739 		}
740 		if (error->error == KRB_ERR_GENERIC) {
741 			if (error->text.data)
742 				fprintf(stderr,
743 				gettext("Generic remote error: %s\n"),
744 					error->text.data);
745 		} else if (error->error) {
746 			com_err(progname,
747 				(krb5_error_code) error->error +
748 				  ERROR_TABLE_BASE_krb5,
749 				gettext("signalled from server"));
750 			if (error->text.data)
751 				fprintf(stderr,
752 				gettext("Error text from server: %s\n"),
753 					error->text.data);
754 		}
755 		krb5_free_error(context, error);
756 		exit(1);
757 	}
758 
759 	retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL);
760 	if (retval) {
761 		com_err(progname, retval,
762 			gettext("while decoding final size packet from server"));
763 		exit(1);
764 	}
765 
766 	memcpy((char *)&send_size, outbuf.data, sizeof(send_size));
767 	send_size = ntohl(send_size);
768 	if (send_size != database_size) {
769 		com_err(progname, 0,
770 			gettext("Kpropd sent database size %d, expecting %d"),
771 			send_size, database_size);
772 		exit(1);
773 	}
774 	free(outbuf.data);
775 	free(inbuf.data);
776 }
777 
778 void
779 send_error(context, my_creds, fd, err_text, err_code)
780     krb5_context context;
781     krb5_creds *my_creds;
782     int	fd;
783     char	*err_text;
784     krb5_error_code	err_code;
785 {
786 	krb5_error	error;
787 	const char	*text;
788 	krb5_data	outbuf;
789 
790 	memset((char *)&error, 0, sizeof(error));
791 	krb5_us_timeofday(context, &error.ctime, &error.cusec);
792 	error.server = my_creds->server;
793 	error.client = my_principal;
794 	error.error = err_code - ERROR_TABLE_BASE_krb5;
795 	if (error.error > 127)
796 		error.error = KRB_ERR_GENERIC;
797 	if (err_text)
798 		text = err_text;
799 	else
800 		text = error_message(err_code);
801 	error.text.length = strlen(text) + 1;
802 	error.text.data = malloc((unsigned int) error.text.length);
803 	if (error.text.data) {
804 		strcpy(error.text.data, text);
805 		if (!krb5_mk_error(context, &error, &outbuf)) {
806 			(void) krb5_write_message(context, (void *)&fd,&outbuf);
807 			krb5_free_data_contents(context, &outbuf);
808 		}
809 		free(error.text.data);
810 	}
811 }
812 
813 void update_last_prop_file(hostname, file_name)
814 	char *hostname;
815 	char *file_name;
816 {
817 	/* handle slave locking/failure stuff */
818 	char *file_last_prop;
819 	int fd;
820 	static char last_prop[]=".last_prop";
821 
822 	if ((file_last_prop = (char *)malloc(strlen(file_name) +
823 					     strlen(hostname) + 1 +
824 					     strlen(last_prop) + 1)) == NULL) {
825 		com_err(progname, ENOMEM,
826 			gettext("while allocating filename for update_last_prop_file"));
827 		return;
828 	}
829 	strcpy(file_last_prop, file_name);
830 
831 	/*
832 	 * If a nondefault file name was specified then we should not add an
833 	 * extraneous host name to the file name given that a file name could
834 	 * have already specified a host name and therefore would be redundant.
835 	 */
836 	if (strcmp(file_name, KPROP_DEFAULT_FILE) == 0) {
837 	strcat(file_last_prop, ".");
838 	strcat(file_last_prop, hostname);
839 	}
840 	strcat(file_last_prop, last_prop);
841 	if ((fd = THREEPARAMOPEN(file_last_prop, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
842 		com_err(progname, errno,
843 			gettext("while creating 'last_prop' file, '%s'"),
844 			file_last_prop);
845 		free(file_last_prop);
846 		return;
847 	}
848 	write(fd, "", 1);
849 	free(file_last_prop);
850 	close(fd);
851 	return;
852 }
853