2 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
8 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
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.
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.
40 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <sys/param.h>
53 static char *kprop_version
= KPROP_PROT_VERSION
;
60 char *file
= KPROP_DEFAULT_FILE
;
63 krb5_principal my_principal
; /* The Kerberos principal we'll be */
64 /* running under, initialized in */
66 krb5_ccache ccache
; /* Credentials cache which we'll be using */
68 krb5_address sender_addr
;
69 krb5_address receiver_addr
;
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
**);
83 (krb5_context
, char *, int *);
87 (krb5_context
, krb5_auth_context
, krb5_creds
*,
90 (krb5_context
, krb5_creds
*, int, char *, krb5_error_code
);
91 void update_last_prop_file
98 ("\nUsage: %s [-r realm] [-f file] [-d] [-P port] [-s srvtab] slave_host\n\n"),
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
];
116 (void) setlocale(LC_ALL
, "");
118 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
119 #define TEXT_DOMAIN "KPROP_TEST" /* Use this only if it weren't */
122 (void) textdomain(TEXT_DOMAIN
);
124 retval
= krb5_init_context(&context
);
126 com_err(argv
[0], retval
, gettext("while initializing krb5"));
130 get_tickets(context
);
132 database_fd
= open_database(context
, file
, &database_size
);
133 retval
= open_connection(slave_host
, &fd
, Errmsg
, sizeof(Errmsg
));
135 com_err(progname
, retval
, gettext("%s while opening connection to %s"),
141 gettext("%s: %s while opening connection to %s\n"),
142 progname
, Errmsg
, slave_host
);
145 kerberos_authenticate(context
, &auth_context
, fd
, my_principal
,
147 xmit_database(context
, auth_context
, my_creds
, fd
, database_fd
,
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
);
161 register char *word
, ch
;
166 while ((c
= getopt(argc
, argv
, "r:f:dP:s:h:")) != EOF
) {
193 printf (gettext("Error \n"));
198 argv
= &argv
[optind
];
205 void get_tickets(context
)
206 krb5_context context
;
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
;
219 * Figure out what tickets we'll be using to send stuff
222 if ((def_realm
= strdup(realm
)) == NULL
) {
223 com_err(progname
, ENOMEM
,
224 gettext("while allocating default realm name '%s'"),
229 retval
= krb5_get_default_realm(context
, &def_realm
);
231 com_err(progname
, retval
,
232 gettext("while getting default realm"));
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.
242 retval
= kadm5_get_master(context
, def_realm
, &master_host
);
245 com_err(progname
, retval
,
246 gettext("while getting admin server fqdn"));
250 retval
= krb5_sname_to_principal(context
, master_host
, NULL
,
251 KRB5_NT_SRV_HST
, &my_principal
);
256 com_err(progname
, errno
, gettext("while setting client principal name"));
260 retval
= krb5_set_principal_realm(context
, my_principal
, realm
);
262 com_err(progname
, errno
,
263 gettext("while setting client principal realm"));
268 krb5_princ_type(context
, my_principal
) = KRB5_NT_PRINCIPAL
;
272 * Initialize cache file which we're going to be using
274 (void) mktemp(tkstring
);
275 snprintf(buf
, sizeof (buf
), "FILE:%s", tkstring
);
277 retval
= krb5_cc_resolve(context
, buf
, &ccache
);
279 com_err(progname
, retval
, gettext("while opening credential cache %s"),
284 retval
= krb5_cc_initialize(context
, ccache
, my_principal
);
286 com_err (progname
, retval
, gettext("when initializing cache %s"),
292 * Get the tickets we'll need.
294 * Construct the principal name for the slave host.
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
);
301 com_err(progname
, errno
, gettext("while setting server principal name"));
302 (void) krb5_cc_destroy(context
, ccache
);
306 retval
= krb5_set_principal_realm(context
, creds
.server
, realm
);
308 com_err(progname
, errno
,
309 gettext("while setting server principal realm"));
315 * Now fill in the client....
317 retval
= krb5_copy_principal(context
, my_principal
, &creds
.client
);
319 com_err(progname
, retval
, gettext("While copying client principal"));
320 (void) krb5_cc_destroy(context
, ccache
);
324 retval
= krb5_kt_resolve(context
, srvtab
, &keytab
);
326 com_err(progname
, retval
, gettext("while resolving keytab"));
327 (void) krb5_cc_destroy(context
, ccache
);
331 (void) memset(&opt
, 0, sizeof (opt
));
332 krb5_get_init_creds_opt_init(&opt
);
333 retval
= krb5_unparse_name(context
, creds
.server
, &svcname
);
335 com_err(progname
, errno
, gettext("while parsing svc principal name"));
336 (void) krb5_cc_destroy(context
, ccache
);
339 retval
= krb5_get_init_creds_keytab(context
, &creds
, creds
.client
,
340 keytab
, 0, svcname
, &opt
);
346 com_err(progname
, retval
, gettext("while getting initial ticket\n"));
347 (void) krb5_cc_destroy(context
, ccache
);
352 (void) krb5_kt_close(context
, keytab
);
355 * Now destroy the cache right away --- the credentials we
356 * need will be in my_creds.
358 retval
= krb5_cc_destroy(context
, ccache
);
360 com_err(progname
, retval
, gettext("while destroying ticket cache"));
365 /* SUNW14resync - SOCKET is defed in 1.4 in port-sockets.h */
371 open_connection(host
, fd
, Errmsg
, ErrmsgSz
)
375 unsigned int ErrmsgSz
;
378 krb5_error_code retval
;
381 struct addrinfo hints
, *ai
, *aitop
;
382 struct sockaddr_storage ss
;
383 char serv_or_port
[NI_MAXSERV
];
384 enum err_types
{SOCKET
, CONNECT
};
387 memset(&hints
, 0, sizeof(hints
));
388 hints
.ai_family
= AF_UNSPEC
; /* go for either IPv4 or v6 */
389 hints
.ai_socktype
= SOCK_STREAM
;
392 (void) snprintf(serv_or_port
, sizeof(serv_or_port
), ("%hu"),
395 strncpy(serv_or_port
, KPROP_SERVICE
, sizeof(serv_or_port
));
397 if (getaddrinfo(host
, serv_or_port
, &hints
, &aitop
) != 0) {
398 (void) snprintf(Errmsg
, ERRMSGSIZ
, gettext("%s: unknown host"),
404 for (ai
= aitop
; ai
; ai
= ai
->ai_next
) {
405 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
408 s
= socket(ai
->ai_family
, SOCK_STREAM
, 0);
415 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0 &&
416 errno
!= EINPROGRESS
) {
420 continue; /* fail -- try next */
429 (void) snprintf(Errmsg
, ERRMSGSIZ
,
430 gettext("in call to socket"));
433 (void) snprintf(Errmsg
, ERRMSGSIZ
,
434 gettext("in call to connect"));
437 retval
= -1; /* generic error */
438 (void) snprintf(Errmsg
, ERRMSGSIZ
,
439 gettext("could not setup network"));
449 * Set receiver_addr and sender_addr.
451 if (cvtkaddr((struct sockaddr_storage
*)ai
->ai_addr
, &receiver_addr
)
454 com_err(progname
, errno
,
455 gettext("while converting socket address"));
463 socket_length
= sizeof(ss
);
464 if (getsockname(s
, (struct sockaddr
*)&ss
, &socket_length
) < 0) {
467 (void) snprintf(Errmsg
, ERRMSGSIZ
,
468 gettext("in call to getsockname"));
472 if (cvtkaddr(&ss
, &sender_addr
) == NULL
) {
474 com_err(progname
, errno
,
475 gettext("while converting socket address"));
483 void kerberos_authenticate(context
, auth_context
, fd
, me
, new_creds
)
484 krb5_context context
;
485 krb5_auth_context
*auth_context
;
488 krb5_creds
** new_creds
;
490 krb5_error_code retval
;
491 krb5_error
*error
= NULL
;
492 krb5_ap_rep_enc_part
*rep_result
;
494 retval
= krb5_auth_con_init(context
, auth_context
);
498 krb5_auth_con_setflags(context
, *auth_context
,
499 KRB5_AUTH_CONTEXT_DO_SEQUENCE
);
501 retval
= krb5_auth_con_setaddrs(context
, *auth_context
, &sender_addr
,
504 com_err(progname
, retval
, gettext("in krb5_auth_con_setaddrs"));
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
);
513 com_err(progname
, retval
, gettext("while authenticating to server"));
515 if (error
->error
== KRB_ERR_GENERIC
) {
516 if (error
->text
.data
)
518 gettext("Generic remote error: %s\n"),
520 } else if (error
->error
) {
522 (krb5_error_code
) error
->error
+ ERROR_TABLE_BASE_krb5
,
523 gettext("signalled from server"));
524 if (error
->text
.data
)
526 gettext("Error text from server: %s\n"),
529 krb5_free_error(context
, error
);
533 krb5_free_ap_rep_enc_part(context
, rep_result
);
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
542 * Returns the file descriptor of the database dump file. Also fills
543 * in the size of the database file.
546 open_database(context
, data_fn
, size
)
547 krb5_context context
;
553 struct stat stbuf
, stbuf_ok
;
555 static char ok
[] = ".dump_ok";
557 dbpathname
= strdup(data_fn
);
559 com_err(progname
, ENOMEM
, gettext("allocating database file name '%s'"),
563 if ((fd
= open(dbpathname
, O_RDONLY
)) < 0) {
564 com_err(progname
, errno
, gettext("while trying to open %s"),
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"));
575 com_err(progname
, err
, gettext("while trying to lock '%s'"), dbpathname
);
578 if (fstat(fd
, &stbuf
)) {
579 com_err(progname
, errno
, gettext("while trying to stat %s"),
583 if ((data_ok_fn
= (char *) malloc(strlen(data_fn
)+strlen(ok
)+1))
585 com_err(progname
, ENOMEM
, gettext("while trying to malloc data_ok_fn"));
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"),
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
);
602 *size
= stbuf
.st_size
;
607 close_database(context
, fd
)
608 krb5_context context
;
612 err
= krb5_lock_file(context
, fd
, KRB5_LOCKMODE_UNLOCK
);
614 com_err(progname
, err
, gettext("while unlocking database '%s'"), dbpathname
);
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.
626 * At any point in the protocol, we may send a KRB_ERROR message; this
627 * will abort the entire operation.
630 xmit_database(context
, auth_context
, my_creds
, fd
, database_fd
,
632 krb5_context context
;
633 krb5_auth_context auth_context
;
634 krb5_creds
*my_creds
;
637 int in_database_size
;
639 krb5_int32 sent_size
, n
;
640 krb5_data inbuf
, outbuf
;
641 char buf
[KPROP_BUFSIZ
];
642 krb5_error_code retval
;
644 /* These must be 4 bytes */
645 krb5_ui_4 database_size
= in_database_size
;
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
,
658 com_err(progname
, retval
, gettext("while encoding database size"));
659 send_error(context
, my_creds
, fd
, gettext("while encoding database size"), retval
);
663 retval
= krb5_write_message(context
, (void *) &fd
, &outbuf
);
665 krb5_free_data_contents(context
, &outbuf
);
666 com_err(progname
, retval
, gettext("while sending database size"));
669 krb5_free_data_contents(context
, &outbuf
);
671 * Initialize the initial vector.
673 retval
= krb5_auth_con_initivector(context
, auth_context
);
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"));
682 * Send over the file, block by block....
686 while ((n
= read(database_fd
, buf
, sizeof(buf
)))) {
688 retval
= krb5_mk_priv(context
, auth_context
, &inbuf
,
691 snprintf(buf
, sizeof (buf
),
692 gettext("while encoding database block starting at %d"),
694 com_err(progname
, retval
, buf
);
695 send_error(context
, my_creds
, fd
, buf
, retval
);
699 retval
= krb5_write_message(context
, (void *)&fd
,&outbuf
);
701 krb5_free_data_contents(context
, &outbuf
);
702 com_err(progname
, retval
,
703 gettext("while sending database block starting at %d"),
707 krb5_free_data_contents(context
, &outbuf
);
710 printf(gettext("%d bytes sent.\n"), sent_size
);
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
);
720 * OK, we've sent the database; now let's wait for a success
721 * indication from the remote end.
723 retval
= krb5_read_message(context
, (void *) &fd
, &inbuf
);
725 com_err(progname
, retval
,
726 gettext("while reading response from server"));
730 * If we got an error response back from the server, display
733 if (krb5_is_krb_error(&inbuf
)) {
734 retval
= krb5_rd_error(context
, &inbuf
, &error
);
736 com_err(progname
, retval
,
737 gettext("while decoding error response from server"));
740 if (error
->error
== KRB_ERR_GENERIC
) {
741 if (error
->text
.data
)
743 gettext("Generic remote error: %s\n"),
745 } else if (error
->error
) {
747 (krb5_error_code
) error
->error
+
748 ERROR_TABLE_BASE_krb5
,
749 gettext("signalled from server"));
750 if (error
->text
.data
)
752 gettext("Error text from server: %s\n"),
755 krb5_free_error(context
, error
);
759 retval
= krb5_rd_safe(context
,auth_context
,&inbuf
,&outbuf
,NULL
);
761 com_err(progname
, retval
,
762 gettext("while decoding final size packet from server"));
766 memcpy((char *)&send_size
, outbuf
.data
, sizeof(send_size
));
767 send_size
= ntohl(send_size
);
768 if (send_size
!= database_size
) {
770 gettext("Kpropd sent database size %d, expecting %d"),
771 send_size
, database_size
);
779 send_error(context
, my_creds
, fd
, err_text
, err_code
)
780 krb5_context context
;
781 krb5_creds
*my_creds
;
784 krb5_error_code err_code
;
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
;
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
);
809 free(error
.text
.data
);
813 void update_last_prop_file(hostname
, file_name
)
817 /* handle slave locking/failure stuff */
818 char *file_last_prop
;
820 static char last_prop
[]=".last_prop";
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"));
829 strcpy(file_last_prop
, file_name
);
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.
836 if (strcmp(file_name
, KPROP_DEFAULT_FILE
) == 0) {
837 strcat(file_last_prop
, ".");
838 strcat(file_last_prop
, hostname
);
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'"),
845 free(file_last_prop
);
849 free(file_last_prop
);