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