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
usage()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
main(argc,argv)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
PRS(argc,argv)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
get_tickets(context)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
open_connection(host,fd,Errmsg,ErrmsgSz)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 = 0;
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 = -1;
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
kerberos_authenticate(context,auth_context,fd,me,new_creds)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
open_database(context,data_fn,size)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,
560 gettext("allocating database file name '%s'"), 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
close_database(context,fd)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
xmit_database(context,auth_context,my_creds,fd,database_fd,in_database_size)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
send_error(context,my_creds,fd,err_text,err_code)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
update_last_prop_file(hostname,file_name)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