2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * Export of this software from the United States of America may require
8 * a specific license from the United States Government. It is the
9 * responsibility of any person or organization contemplating export to
10 * obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of FundsXpress. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. FundsXpress makes no representations about the suitability of
20 * this software for any purpose. It is provided "as is" without express
21 * or implied warranty.
23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
32 * All Rights Reserved.
34 * Export of this software from the United States of America may
35 * require a specific license from the United States Government.
36 * It is the responsibility of any person or organization contemplating
37 * export to obtain such a license before exporting.
39 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
40 * distribute this software and its documentation for any purpose and
41 * without fee is hereby granted, provided that the above copyright
42 * notice appear in all copies and that both that copyright notice and
43 * this permission notice appear in supporting documentation, and that
44 * the name of M.I.T. not be used in advertising or publicity pertaining
45 * to distribution of the software without specific, written prior
46 * permission. Furthermore if you modify this software you must label
47 * your software as modified software and not distribute it in such a
48 * fashion that it might be confused with the original M.I.T. software.
49 * M.I.T. makes no representations about the suitability of
50 * this software for any purpose. It is provided "as is" without express
51 * or implied warranty.
54 * XXX We need to modify the protocol so that an acknowledge is set
55 * after each block, instead after the entire series is sent over.
56 * The reason for this is so that error packets can get interpreted
57 * right away. If you don't do this, the sender may never get the
58 * error packet, because it will die an EPIPE trying to complete the
69 #include <sys/types.h>
72 #include <sys/socket.h>
74 #include <netinet/in.h>
75 #include <arpa/inet.h>
76 #include <sys/param.h>
82 #include <socket-utils.h>
87 #include <iprop_hdr.h>
89 #include <kadm5/admin.h>
90 #include <kdb/kdb_log.h>
92 /* Solaris Kerberos */
95 #define SYSLOG_CLASS LOG_DAEMON
96 #define INITIAL_TIMER 10
98 char *poll_time
= NULL
;
99 char *def_realm
= NULL
;
100 boolean_t runonce
= B_FALSE
;
103 * Global fd to close upon alarm time-out.
105 volatile int gfd
= -1;
108 * This struct simulates the use of _kadm5_server_handle_t
110 typedef struct _kadm5_iprop_handle_t
{
111 krb5_ui_4 magic_number
;
112 krb5_ui_4 struct_version
;
113 krb5_ui_4 api_version
;
117 krb5_context context
;
118 kadm5_config_params params
;
119 struct _kadm5_iprop_handle_t
*lhandle
;
120 } *kadm5_iprop_handle_t
;
122 static char *kprop_version
= KPROP_PROT_VERSION
;
129 krb5_principal server
; /* This is our server principal name */
130 krb5_principal client
; /* This is who we're talking to */
131 krb5_context kpropd_context
;
132 krb5_auth_context auth_context
;
133 char *realm
= NULL
; /* Our realm */
134 char *file
= KPROPD_DEFAULT_FILE
;
135 char *temp_file_name
;
136 char *kdb5_util
= KPROPD_DEFAULT_KDB5_UTIL
;
137 char *kerb_database
= NULL
;
138 char *acl_file_name
= KPROPD_ACL_FILE
;
140 krb5_address sender_addr
;
141 krb5_address receiver_addr
;
147 (iprop_role iproprole
);
150 krb5_error_code
do_iprop(kdb_log_context
*log_ctx
);
152 /* Solaris Kerberos */
153 void kerberos_authenticate
158 struct sockaddr_storage
*);
159 krb5_boolean authorized_principal
182 unsigned int backoff_from_master
188 gettext("\nUsage: %s\n"), /* progname may be a long pathname */
192 gettext("\t[-r realm] [-s srvtab] [-dS] [-f slave_file]\n"));
195 gettext("\t[-F kerberos_db_file ] [-p kdb5_util_pathname]\n"));
197 fprintf(stderr
, gettext("\t[-P port] [-a acl_file]\n"));
207 krb5_error_code retval
;
209 kdb_log_context
*log_ctx
;
211 krb5_boolean is_master
= FALSE
;
215 log_ctx
= kpropd_context
->kdblog_context
;
217 if (log_ctx
&& (log_ctx
->iproprole
== IPROP_SLAVE
)) {
219 * We wanna do iprop !
221 retval
= krb5_db_supports_iprop(kpropd_context
,
224 /* Solaris Kerberos: Keep error messages consistent */
225 com_err(progname
, retval
,
226 gettext("while determining if dbmodule plugin "
230 if (!iprop_supported
) {
231 /* Solaris Kerberos: Keep error messages consistent */
233 gettext("Current dbmodule plugin does not support "
240 * Ensure that kpropd is only run on a slave
242 if (retval
= kadm5_is_master(kpropd_context
, def_realm
,
244 com_err(progname
, retval
,
245 gettext("while trying to determine whether host is "
246 "master KDC for realm %s"), def_realm
);
250 if (is_master
== TRUE
) {
252 kadm5_get_master(kpropd_context
, def_realm
, &master
);
255 gettext("%s is the master KDC for the realm %s. "
256 "%s can only be run on a slave KDC"),
257 master
? master
: "unknown", def_realm
, progname
);
261 retval
= do_iprop(log_ctx
);
263 /* Solaris Kerberos: Keep error messages consistent */
264 com_err(progname
, retval
,
265 gettext("while doing iprop"));
273 * Ensure that the kpropd.acl file exists and contains at least
280 tmp_acl_file
= fopen(acl_file_name
, "r");
282 com_err(progname
, errno
,
283 gettext("while opening acl file %s"),
288 while (!feof(tmp_acl_file
) && !seen_file
) {
289 if (!fgets(buf
, sizeof(buf
), tmp_acl_file
))
292 if (buf
[0] != '#' && !isspace(buf
[0]))
297 gettext("No entries found in %s. Can't "
298 "authorize propagation requests"), acl_file_name
);
301 fclose(tmp_acl_file
);
304 ret
= do_standalone(IPROP_NULL
);
312 void resync_alarm(int sn
)
316 fprintf(stderr
, gettext("resync_alarm: closing fd: %d\n"), gfd
);
320 int do_standalone(iprop_role iproprole
)
322 struct linger linger
;
324 int finet
, fromlen
, s
;
327 struct sockaddr_in6 sin6
= { AF_INET6
};
328 int sin6_size
= sizeof (sin6
);
330 * Timer for accept/read calls, in case of network type errors.
332 int backoff_timer
= INITIAL_TIMER
;
336 /* listen for either ipv4 or ipv6 */
337 finet
= socket(AF_INET6
, SOCK_STREAM
, IPPROTO_TCP
);
339 com_err(progname
, errno
, gettext("while obtaining socket"));
344 sp
= getservbyname(KPROP_SERVICE
, "tcp");
346 com_err(progname
, 0, gettext("%s/tcp: unknown service"),
350 sin6
.sin6_port
= sp
->s_port
;
352 sin6
.sin6_port
= port
;
355 * We need to close the socket immediately if iprop is enabled,
356 * since back-to-back full resyncs are possible, so we do not
357 * linger around for too long
359 if (iproprole
== IPROP_SLAVE
) {
360 if (setsockopt(finet
, SOL_SOCKET
, SO_REUSEADDR
,
361 (char *)&on
, sizeof(on
)) < 0)
362 com_err(progname
, errno
,
363 gettext("while setting socket option (SO_REUSEADDR)"));
366 if (setsockopt(finet
, SOL_SOCKET
, SO_LINGER
,
367 (void *)&linger
, sizeof(linger
)) < 0)
368 com_err(progname
, errno
,
369 gettext("while setting socket option (SO_LINGER)"));
371 * We also want to set a timer so that the slave is not waiting
372 * until infinity for an update from the master.
375 signal(SIGALRM
, resync_alarm
);
377 fprintf(stderr
, "do_standalone: setting resync alarm to %d\n",
380 if (alarm(backoff_timer
) != 0) {
383 gettext("%s: alarm already set\n"), progname
);
388 if ((ret
= bind(finet
, (struct sockaddr
*)&sin6
, sizeof(sin6
))) < 0) {
392 gettext("%s: attempting to rebind socket "
393 "with SO_REUSEADDR\n"), progname
);
394 if (setsockopt(finet
, SOL_SOCKET
, SO_REUSEADDR
,
395 (char *)&on
, sizeof(on
)) < 0) {
396 com_err(progname
, errno
,
397 gettext("while setting socket option (SO_REUSEADDR)"));
399 ret
= bind(finet
, (struct sockaddr
*) &sin6
, sizeof(sin6
));
405 * com_err will print the err msg associated with errno
408 perror(gettext("bind"));
410 com_err(progname
, errno
,
411 gettext("while binding listener socket"));
415 if (!debug
&& (iproprole
!= IPROP_SLAVE
)) {
416 /* Solaris Kerberos: Indicate where further messages will be sent */
418 gettext("%s: Logging to SYSLOG with LOG_DAEMON facility\n"),
421 com_err(progname
, errno
, gettext("while daemonizing"));
424 rem_default_com_err_hook();
428 if ((pidfile
= fopen(PID_FILE
, "w")) != NULL
) {
429 fprintf(pidfile
, gettext("%d\n"), getpid());
432 com_err(progname
, errno
,
433 gettext("while opening pid file %s for writing"),
436 if (listen(finet
, 5) < 0) {
437 /* Solaris Kerberos: Keep error messages consistent */
438 com_err(progname
, errno
, gettext("while listening on socket"));
444 s
= accept(finet
, (struct sockaddr
*) &sin6
, &sin6_size
);
450 * Solaris Kerberos: Keep error messages
454 gettext("while accepting connection"));
455 backoff_timer
= INITIAL_TIMER
;
458 * If we got EBADF, an alarm signal handler closed
459 * the file descriptor on us.
464 * An alarm could have been set and the fd closed, we
465 * should retry in case of transient network error for
466 * up to a couple of minutes.
468 if (backoff_timer
> 120)
474 if (debug
&& (iproprole
!= IPROP_SLAVE
))
480 com_err(progname
, errno
, gettext("while forking"));
494 * Errors should not be considered fatal in the iprop case as we
495 * could have transient type errors, such as network outage, etc.
496 * Sleeping 3s for 2s linger interval.
498 if (wait(&status
) < 0) {
499 com_err(progname
, errno
,
500 gettext("while waiting to receive database"));
501 if (iproprole
!= IPROP_SLAVE
)
507 if (iproprole
== IPROP_SLAVE
)
510 if ((ret
= WEXITSTATUS(status
)) != 0)
514 if (iproprole
== IPROP_SLAVE
)
524 struct sockaddr_storage from
;
528 krb5_error_code retval
;
534 char ntop
[NI_MAXHOST
] = "";
535 krb5_context doit_context
;
536 kdb_log_context
*log_ctx
;
538 retval
= krb5_init_context(&doit_context
);
540 com_err(progname
, retval
, gettext("while initializing krb5"));
543 log_ctx
= kpropd_context
->kdblog_context
;
544 if (log_ctx
&& (log_ctx
->iproprole
== IPROP_SLAVE
)) {
545 ulog_set_role(doit_context
, IPROP_SLAVE
);
547 * We also want to set a timer so that the slave is not waiting
548 * until infinity for an update from the master.
551 fprintf(stderr
, "doit: setting resync alarm to %ds\n",
553 signal(SIGALRM
, resync_alarm
);
555 if (alarm(INITIAL_TIMER
) != 0) {
558 gettext("%s: alarm already set\n"), progname
);
563 fromlen
= (socklen_t
)sizeof (from
);
564 if (getpeername(fd
, (struct sockaddr
*) &from
, &fromlen
) < 0) {
565 fprintf(stderr
, "%s: ", progname
);
566 perror(gettext("getpeername"));
569 if (setsockopt(fd
, SOL_SOCKET
, SO_KEEPALIVE
, (caddr_t
) &on
,
571 com_err(progname
, errno
,
572 gettext("while attempting setsockopt (SO_KEEPALIVE)"));
575 if (getnameinfo((struct sockaddr
*)&from
, fromlen
, ntop
, sizeof(ntop
),
576 NULL
, 0, NI_NUMERICHOST
) != 0) {
578 /* getnameifo failed so use inet_ntop() to get printable addresses */
579 if (from
.ss_family
== AF_INET
) {
582 (const void *)&ss2sin(&from
)->sin_addr
,
585 } else if (from
.ss_family
== AF_INET6
&&
586 ! IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from
)->sin6_addr
)) {
591 (const void *)&ss2sin6(&from
)->sin6_addr
, ntop
,
594 /* ipv4 mapped ipv6 addrs handled later */
597 if (from
.ss_family
== AF_INET
|| from
.ss_family
== AF_INET6
) {
599 if (from
.ss_family
== AF_INET6
&&
600 IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from
)->sin6_addr
)) {
604 /* coerce ipv4 mapped ipv6 addr to normal ipv4 addr */
605 IN6_V4MAPPED_TO_IPADDR(&(ss2sin6(&from
)->sin6_addr
),
608 inet_ntop(AF_INET
, (const void *) &v4addr
,
612 syslog(LOG_INFO
, gettext("Connection from %s"), ntop
);
615 printf("Connection from %s\n", ntop
);
618 /* address family isn't either AF_INET || AF_INET6 */
620 gettext("Connection from unknown address family:%d"),
624 printf(gettext("Connection from unknown address family:%d"),
630 * Now do the authentication
632 /* Solaris Kerberos */
633 kerberos_authenticate(doit_context
, fd
, &client
, &etype
, &from
);
636 * Turn off alarm upon successful authentication from master.
641 if (!authorized_principal(doit_context
, client
, etype
)) {
644 retval
= krb5_unparse_name(doit_context
, client
, &name
);
646 /* Solaris Kerberos: Keep error messages consistent */
647 com_err(progname
, retval
,
648 gettext("while unparsing client name"));
652 gettext("Rejected connection from unauthorized principal %s"),
658 lock_fd
= open(temp_file_name
, O_RDWR
|O_CREAT
, 0600);
660 retval
= krb5_lock_file(doit_context
, lock_fd
,
661 KRB5_LOCKMODE_EXCLUSIVE
|KRB5_LOCKMODE_DONTBLOCK
);
663 com_err(progname
, retval
,
664 gettext("while trying to lock '%s'"),
668 if ((database_fd
= open(temp_file_name
,
669 O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) < 0) {
670 com_err(progname
, errno
,
671 gettext("while opening database file, '%s'"),
675 recv_database(doit_context
, fd
, database_fd
, &confmsg
);
676 if (rename(temp_file_name
, file
)) {
677 /* Solaris Kerberos: Keep error messages consistent */
678 com_err(progname
, errno
,
679 gettext("while renaming %s to %s"),
680 temp_file_name
, file
);
683 retval
= krb5_lock_file(doit_context
, lock_fd
, KRB5_LOCKMODE_SHARED
);
685 com_err(progname
, retval
,
686 gettext("while downgrading lock on '%s'"),
690 load_database(doit_context
, kdb5_util
, file
);
691 retval
= krb5_lock_file(doit_context
, lock_fd
, KRB5_LOCKMODE_UNLOCK
);
693 com_err(progname
, retval
,
694 gettext("while unlocking '%s'"), temp_file_name
);
697 (void)close(lock_fd
);
700 * Send the acknowledgement message generated in
701 * recv_database, then close the socket.
703 retval
= krb5_write_message(doit_context
, (void *) &fd
, &confmsg
);
705 krb5_free_data_contents(doit_context
, &confmsg
);
706 com_err(progname
, retval
,
707 gettext("while sending # of received bytes"));
710 krb5_free_data_contents(doit_context
, &confmsg
);
712 com_err(progname
, errno
,
713 gettext("while trying to close database file"));
722 * Routine to handle incremental update transfer(s) from master KDC
724 krb5_error_code
do_iprop(kdb_log_context
*log_ctx
) {
727 kadm5_config_params params
;
729 krb5_principal iprop_svc_principal
;
730 void *server_handle
= NULL
;
731 char *iprop_svc_princstr
= NULL
;
732 char *master_svc_princstr
= NULL
;
733 char *admin_server
= NULL
;
734 char *keytab_name
= NULL
;
735 unsigned int pollin
, backoff_time
;
739 boolean_t frdone
= B_FALSE
;
741 kdb_incr_result_t
*incr_ret
;
742 static kdb_last_t mylast
;
744 kdb_fullresync_result_t
*full_ret
;
745 char *full_resync_arg
= NULL
;
747 kadm5_iprop_handle_t handle
;
751 krb5_keytab_entry entry
;
752 char kt_name
[MAX_KEYTAB_NAME_LEN
];
756 * Delay daemonizing until some basic configuration checks have been
763 pollin
= (unsigned int)0;
764 (void) memset((char *)¶ms
, 0, sizeof (params
));
765 ulog
= log_ctx
->ulog
;
767 params
.mask
|= KADM5_CONFIG_REALM
;
768 params
.realm
= def_realm
;
770 if (master_svc_princstr
== NULL
) {
771 if (retval
= kadm5_get_kiprop_host_srv_name(kpropd_context
,
772 def_realm
, &master_svc_princstr
)) {
773 /* Solaris Kerberos: keep error messages consistent */
774 com_err(progname
, retval
,
775 gettext("while getting kiprop host based "
776 "service name for realm %s"), def_realm
);
782 * Set cc to the default credentials cache
784 if (retval
= krb5_cc_default(kpropd_context
, &cc
)) {
785 com_err(progname
, retval
,
786 gettext("while opening default "
787 "credentials cache"));
791 retval
= krb5_sname_to_principal(kpropd_context
, NULL
, KIPROP_SVC_NAME
,
792 KRB5_NT_SRV_HST
, &iprop_svc_principal
);
794 com_err(progname
, retval
, gettext("while trying to construct "
795 "host service principal"));
799 /* Solaris Kerberos */
800 if (krb5_is_referral_realm(krb5_princ_realm(kpropd_context
,
801 iprop_svc_principal
))) {
802 krb5_data
*r
= krb5_princ_realm(kpropd_context
,
803 iprop_svc_principal
);
804 assert(def_realm
!= NULL
);
805 r
->length
= strlen(def_realm
);
806 r
->data
= strdup(def_realm
);
807 if (r
->data
== NULL
) {
808 com_err(progname
, retval
,
809 ("while determining local service principal name"));
814 if (retval
= krb5_unparse_name(kpropd_context
, iprop_svc_principal
,
815 &iprop_svc_princstr
)) {
816 com_err(progname
, retval
,
817 gettext("while canonicalizing "
819 krb5_free_principal(kpropd_context
, iprop_svc_principal
);
825 * Check to see if kiprop/<fqdn>@REALM is in the keytab
828 if (retval
= krb5_kt_default_name(kpropd_context
, kt_name
,
829 MAX_KEYTAB_NAME_LEN
)){
830 com_err(progname
, retval
, gettext ("while resolving the "
831 "name of the default keytab"));
834 if (retval
= krb5_kt_default(kpropd_context
, &kt
)) {
835 com_err(progname
, retval
, gettext ("while resolving default "
837 krb5_free_principal(kpropd_context
, iprop_svc_principal
);
841 if (retval
= krb5_kt_get_entry(kpropd_context
, kt
, iprop_svc_principal
,
843 com_err(progname
, retval
, gettext("while retrieving entry %s "
844 "from %s"), iprop_svc_princstr
,
845 kt_name
[0] ? kt_name
: "default keytab");
846 krb5_kt_close(kpropd_context
,kt
);
847 krb5_free_principal(kpropd_context
, iprop_svc_principal
);
851 krb5_kt_close(kpropd_context
,kt
);
852 krb5_free_principal(kpropd_context
, iprop_svc_principal
);
855 /* Solaris Kerberos: Indicate where further messages will be sent */
856 fprintf(stderr
, gettext("%s: Logging to SYSLOG\n"), progname
);
858 com_err(progname
, errno
, gettext("while daemonizing"));
861 rem_default_com_err_hook();
866 * Authentication, initialize rpcsec_gss handle etc.
868 retval
= kadm5_init_with_skey(iprop_svc_princstr
, keytab_name
,
871 KADM5_STRUCT_VERSION
,
877 if (retval
== KADM5_RPC_ERROR
) {
880 kadm5_destroy((void *) server_handle
);
881 server_handle
= (void *)NULL
;
882 handle
= (kadm5_iprop_handle_t
)NULL
;
884 com_err(progname
, retval
, gettext(
885 "while attempting to connect"
886 " to master KDC ... retrying"));
887 backoff_time
= backoff_from_master(&reinit_cnt
);
888 (void) sleep(backoff_time
);
891 /* Solaris Kerberos: Be more verbose */
892 com_err(progname
, retval
,
893 gettext("while initializing %s interface for "
894 "%s"), progname
, iprop_svc_princstr
);
895 if (retval
== KADM5_BAD_CLIENT_PARAMS
||
896 retval
== KADM5_BAD_SERVER_PARAMS
)
903 * Reset re-initialization count to zero now.
905 reinit_cnt
= backoff_time
= 0;
908 * Reset the handle to the correct type for the RPC call
910 handle
= server_handle
;
913 * If we have reached this far, we have succesfully established
914 * a RPCSEC_GSS connection; we now start polling for updates
916 if (poll_time
== NULL
) {
917 if ((poll_time
= (char *)strdup("2m")) == NULL
) {
918 /* Solaris Kerberos: Keep error messages consistent */
919 com_err(progname
, ENOMEM
,
920 gettext("while allocating poll_time"));
925 if (pollin
== (unsigned int)0)
926 pollin
= convert_polltime(poll_time
);
933 * Get the most recent ulog entry sno + ts, which
934 * we package in the request to the master KDC
936 mylast
.last_sno
= ulog
->kdb_last_sno
;
937 mylast
.last_time
= ulog
->kdb_last_time
;
940 * Loop continuously on an iprop_get_updates_1(),
941 * so that we can keep probing the master for updates
942 * or (if needed) do a full resync of the krb5 db.
945 incr_ret
= iprop_get_updates_1(&mylast
, handle
->clnt
);
946 if (incr_ret
== (kdb_incr_result_t
*)NULL
) {
947 clnt_perror(handle
->clnt
,
948 "iprop_get_updates call failed");
950 kadm5_destroy((void *)server_handle
);
951 server_handle
= (void *)NULL
;
952 handle
= (kadm5_iprop_handle_t
)NULL
;
956 switch (incr_ret
->ret
) {
958 case UPDATE_FULL_RESYNC_NEEDED
:
960 * We dont do a full resync again, if the last
961 * X'fer was a resync and if the master sno is
962 * still "0", i.e. no updates so far.
964 if ((frdone
== B_TRUE
) && (incr_ret
->lastentry
.last_sno
969 full_ret
= iprop_full_resync_1((void *)
970 &full_resync_arg
, handle
->clnt
);
972 if (full_ret
== (kdb_fullresync_result_t
*)
974 clnt_perror(handle
->clnt
,
975 "iprop_full_resync call failed");
977 kadm5_destroy((void *)
979 server_handle
= (void *)NULL
;
980 handle
= (kadm5_iprop_handle_t
)NULL
;
985 switch (full_ret
->ret
) {
989 * We now listen on the kprop port for
992 ret
= do_standalone(log_ctx
->iproprole
);
996 gettext("Full resync "
997 "was unsuccessful\n"));
1000 gettext("Full resync "
1001 "was successful\n"));
1004 gettext("kpropd: Full resync, "
1005 "invalid return."));
1007 * Start backing-off immediately after
1018 * Exponential backoff
1023 case UPDATE_FULL_RESYNC_NEEDED
:
1028 syslog(LOG_ERR
, gettext("kpropd: Full resync,"
1029 " invalid return from master KDC."));
1032 case UPDATE_PERM_DENIED
:
1033 syslog(LOG_ERR
, gettext("kpropd: Full resync,"
1034 " permission denied."));
1038 syslog(LOG_ERR
, gettext("kpropd: Full resync,"
1039 " error returned from master KDC."));
1049 * ulog_replay() will convert the ulog updates to db
1050 * entries using the kdb conv api and will commit
1051 * the entries to the slave kdc database
1053 retval
= ulog_replay(kpropd_context
, incr_ret
);
1056 syslog(LOG_ERR
, gettext("kpropd: ulog_replay"
1057 " failed, updates not registered."));
1062 fprintf(stderr
, gettext("Update transfer "
1063 "from master was OK\n"));
1066 case UPDATE_PERM_DENIED
:
1067 syslog(LOG_ERR
, gettext("kpropd: get_updates,"
1068 " permission denied."));
1072 syslog(LOG_ERR
, gettext("kpropd: get_updates, error "
1073 "returned from master KDC."));
1078 * Exponential backoff
1085 * Master-slave are in sync
1088 fprintf(stderr
, gettext("Master, slave KDC's "
1089 "are in-sync, no updates\n"));
1096 syslog(LOG_ERR
, gettext("kpropd: get_updates,"
1097 " invalid return from master KDC."));
1101 if (runonce
== B_TRUE
)
1105 * Sleep for the specified poll interval (Default is 2 mts),
1106 * or do a binary exponential backoff if we get an
1107 * UPDATE_BUSY signal
1109 if (backoff_cnt
> 0) {
1110 backoff_time
= backoff_from_master(&backoff_cnt
);
1112 fprintf(stderr
, gettext("Busy signal received "
1113 "from master, backoff for %d secs\n"),
1115 (void) sleep(backoff_time
);
1118 (void) sleep(pollin
);
1125 fprintf(stderr
, gettext("ERROR returned by master, bailing\n"));
1126 syslog(LOG_ERR
, gettext("kpropd: ERROR returned by master KDC,"
1131 if(iprop_svc_princstr
)
1132 free(iprop_svc_princstr
);
1133 if (master_svc_princstr
)
1134 free(master_svc_princstr
);
1135 if (retval
= krb5_cc_close(kpropd_context
, cc
)) {
1136 com_err(progname
, retval
,
1137 gettext("while closing default ccache"));
1143 kadm5_destroy((void *)server_handle
);
1145 krb5_free_context(kpropd_context
);
1147 if (runonce
== B_TRUE
)
1155 * Do exponential backoff, since master KDC is BUSY or down
1157 unsigned int backoff_from_master(int *cnt
) {
1160 btime
= (unsigned int)(2<<(*cnt
));
1161 if (btime
> MAX_BACKOFF
) {
1162 btime
= MAX_BACKOFF
;
1171 * Routine to convert the `pollstr' string to seconds
1173 int convert_polltime(char *pollstr
) {
1174 char *tokenptr
= NULL
;
1179 if ((len
= strcspn(pollstr
, "s")) < strlen(pollstr
)) {
1180 tokenptr
= malloc((len
+ 1) * sizeof(char));
1181 (void) strlcpy(tokenptr
, pollstr
, len
+ 1);
1182 polltime
= atoi(tokenptr
);
1185 if ((len
= strcspn(pollstr
, "m")) < strlen(pollstr
)) {
1186 tokenptr
= malloc((len
+ 1) * sizeof(char));
1187 (void) strlcpy(tokenptr
, pollstr
, len
+ 1);
1188 polltime
= atoi(tokenptr
) * 60;
1191 if ((len
= strcspn(pollstr
, "h")) < strlen(pollstr
)) {
1192 tokenptr
= malloc((len
+ 1) * sizeof(char));
1193 (void) strlcpy(tokenptr
, pollstr
, len
+ 1);
1194 polltime
= atoi(tokenptr
) * 3600;
1197 if (tokenptr
!= NULL
)
1200 * If we have a bogus pollstr value, set polltime to the
1201 * default of 2 mts (120 seconds).
1209 kpropd_com_err_proc(whoami
, code
, fmt
, args
)
1215 char error_buf
[8096];
1217 error_buf
[0] = '\0';
1219 vsprintf(error_buf
, fmt
, args
);
1220 syslog(LOG_ERR
, "%s%s%s%s%s", whoami
? whoami
: "", whoami
? ": " : "",
1221 code
? error_message(code
) : "", code
? " " : "", error_buf
);
1228 register char *word
, ch
;
1232 char my_host_name
[MAXHOSTNAMELEN
], buf
[BUFSIZ
];
1233 krb5_error_code retval
;
1234 static const char tmp
[] = ".temp";
1235 kadm5_config_params params
;
1237 (void) setlocale(LC_ALL
, "");
1239 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
1240 #define TEXT_DOMAIN "KPROPD_TEST" /* Use this only if it weren't */
1243 (void) textdomain(TEXT_DOMAIN
);
1245 (void) memset((char *) ¶ms
, 0, sizeof (params
));
1247 retval
= krb5_init_context(&kpropd_context
);
1249 com_err(argv
[0], retval
,
1250 gettext("while initializing krb5"));
1254 /* Solaris Kerberos: Sanitize progname */
1255 progname
= basename(argv
[0]);
1257 while ((c
= getopt(argc
, argv
, "dtf:F:p:P:r:s:Sa:")) != EOF
){
1264 * Undocumented option - for testing only.
1266 * Option to run the kpropd server exactly
1267 * once (this is true only if iprop is enabled).
1278 kerb_database
= optarg
;
1288 port
= htons(atoi(optarg
));
1296 params
.realm
= realm
;
1297 params
.mask
|= KADM5_CONFIG_REALM
;
1308 acl_file_name
= optarg
;
1319 * If not in debug mode, switch com_err reporting to syslog
1322 openlog("kpropd", LOG_PID
| LOG_ODELAY
, SYSLOG_CLASS
);
1325 * Don't replace default logging. Add a new logging channel.
1326 * Stop logging to stderr when daemonizing
1328 add_com_err_hook(kpropd_com_err_proc
);
1331 * Get my hostname, so we can construct my service name
1333 retval
= krb5_sname_to_principal(kpropd_context
,
1334 NULL
, KPROP_SERVICE_NAME
,
1335 KRB5_NT_SRV_HST
, &server
);
1337 /* Solaris Kerberos: Keep error messages consistent */
1338 com_err(progname
, retval
,
1339 gettext("while trying to construct my service name"));
1343 retval
= krb5_set_principal_realm(kpropd_context
, server
, realm
);
1345 com_err(progname
, errno
,
1346 gettext("while constructing my service realm"));
1351 * Construct the name of the temporary file.
1353 if ((temp_file_name
= (char *) malloc(strlen(file
) +
1354 strlen(tmp
) + 1)) == NULL
) {
1355 com_err(progname
, ENOMEM
,
1356 gettext("while allocating filename for temp file"));
1359 strcpy(temp_file_name
, file
);
1360 strcat(temp_file_name
, tmp
);
1362 retval
= kadm5_get_config_params(kpropd_context
, 1, NULL
, ¶ms
,
1365 com_err(progname
, retval
, gettext("while initializing"));
1368 if (params
.iprop_enabled
== TRUE
) {
1369 ulog_set_role(kpropd_context
, IPROP_SLAVE
);
1370 poll_time
= params
.iprop_polltime
;
1372 if (ulog_map(kpropd_context
, ¶ms
, FKPROPD
)) {
1373 /* Solaris Kerberos: Keep error messages consistent */
1374 com_err(progname
, errno
,
1375 gettext("while mapping log"));
1381 * Grab the realm info and check if iprop is enabled.
1383 if (def_realm
== NULL
) {
1384 retval
= krb5_get_default_realm(kpropd_context
, &def_realm
);
1386 /* Solaris Kerberos: Keep error messages consistent */
1387 com_err(progname
, retval
,
1388 gettext("while retrieving default realm"));
1395 * Figure out who's calling on the other end of the connection....
1397 /* Solaris Kerberos */
1399 kerberos_authenticate(context
, fd
, clientp
, etype
, ss
)
1400 krb5_context context
;
1402 krb5_principal
* clientp
;
1403 krb5_enctype
* etype
;
1404 struct sockaddr_storage
* ss
;
1406 krb5_error_code retval
;
1407 krb5_ticket
* ticket
;
1408 struct sockaddr_storage r_ss
;
1410 krb5_keytab keytab
= NULL
;
1413 * Set recv_addr and send_addr
1415 /* Solaris Kerberos */
1416 if (cvtkaddr(ss
, &sender_addr
) == NULL
) {
1417 com_err(progname
, errno
,
1418 gettext("while converting socket address"));
1422 ss_length
= sizeof (r_ss
);
1423 if (getsockname(fd
, (struct sockaddr
*) &r_ss
, &ss_length
)) {
1424 com_err(progname
, errno
,
1425 gettext("while getting local socket address"));
1429 if (cvtkaddr(&r_ss
, &receiver_addr
) == NULL
) {
1430 com_err(progname
, errno
,
1431 gettext("while converting socket address"));
1438 retval
= krb5_unparse_name(context
, server
, &name
);
1440 /* Solaris Kerberos: Keep error messages consistent */
1441 com_err(progname
, retval
, gettext("while unparsing server name"));
1444 printf(gettext("krb5_recvauth(%d, %s, %s, ...)\n"), fd
, kprop_version
,
1449 retval
= krb5_auth_con_init(context
, &auth_context
);
1451 syslog(LOG_ERR
, gettext("Error in krb5_auth_con_init: %s"),
1452 error_message(retval
));
1456 retval
= krb5_auth_con_setflags(context
, auth_context
,
1457 KRB5_AUTH_CONTEXT_DO_SEQUENCE
);
1459 syslog(LOG_ERR
, gettext("Error in krb5_auth_con_setflags: %s"),
1460 error_message(retval
));
1464 retval
= krb5_auth_con_setaddrs(context
, auth_context
, &receiver_addr
,
1467 syslog(LOG_ERR
, gettext("Error in krb5_auth_con_setaddrs: %s"),
1468 error_message(retval
));
1473 retval
= krb5_kt_resolve(context
, srvtab
, &keytab
);
1475 syslog(LOG_ERR
, gettext("Error in krb5_kt_resolve: %s"), error_message(retval
));
1480 retval
= krb5_recvauth(context
, &auth_context
, (void *) &fd
,
1481 kprop_version
, server
, 0, keytab
, &ticket
);
1483 syslog(LOG_ERR
, gettext("Error in krb5_recvauth: %s"), error_message(retval
));
1487 retval
= krb5_copy_principal(context
, ticket
->enc_part2
->client
, clientp
);
1489 syslog(LOG_ERR
, gettext("Error in krb5_copy_prinicpal: %s"),
1490 error_message(retval
));
1494 *etype
= ticket
->enc_part
.enctype
;
1500 retval
= krb5_unparse_name(context
, *clientp
, &name
);
1502 /* Solaris Kerberos: Keep error messages consistent */
1503 com_err(progname
, retval
,
1504 gettext("while unparsing client name"));
1508 retval
= krb5_enctype_to_string(*etype
, etypebuf
, sizeof(etypebuf
));
1510 /* Solaris Kerberos: Keep error messages consistent */
1511 com_err(progname
, retval
, gettext("while unparsing ticket etype"));
1515 printf("authenticated client: %s (etype == %s)\n", name
, etypebuf
);
1519 krb5_free_ticket(context
, ticket
);
1523 authorized_principal(context
, p
, auth_etype
)
1524 krb5_context context
;
1526 krb5_enctype auth_etype
;
1530 krb5_error_code retval
;
1533 krb5_enctype acl_etype
;
1535 retval
= krb5_unparse_name(context
, p
, &name
);
1539 acl_file
= fopen(acl_file_name
, "r");
1543 while (!feof(acl_file
)) {
1544 if (!fgets(buf
, sizeof(buf
), acl_file
))
1546 end
= strlen(buf
) - 1;
1547 if (buf
[end
] == '\n')
1549 if (!strncmp(name
, buf
, strlen(name
))) {
1550 ptr
= buf
+strlen(name
);
1552 /* if the next character is not whitespace or nul, then
1553 the match is only partial. continue on to new lines. */
1554 if (*ptr
&& !isspace((int) *ptr
))
1557 /* otherwise, skip trailing whitespace */
1558 for (; *ptr
&& isspace((int) *ptr
); ptr
++) ;
1560 /* now, look for an etype string. if there isn't one,
1561 return true. if there is an invalid string, continue.
1562 If there is a valid string, return true only if it
1563 matches the etype passed in, otherwise continue */
1566 ((retval
= krb5_string_to_enctype(ptr
, &acl_etype
)) ||
1567 (acl_etype
!= auth_etype
)))
1581 recv_database(context
, fd
, database_fd
, confmsg
)
1582 krb5_context context
;
1587 krb5_ui_4 database_size
; /* This must be 4 bytes */
1588 int received_size
, n
;
1590 krb5_data inbuf
, outbuf
;
1591 krb5_error_code retval
;
1594 * Receive and decode size from client
1596 retval
= krb5_read_message(context
, (void *) &fd
, &inbuf
);
1598 send_error(context
, fd
, retval
, gettext("while reading database size"));
1599 com_err(progname
, retval
,
1600 gettext("while reading size of database from client"));
1603 if (krb5_is_krb_error(&inbuf
))
1604 recv_error(context
, &inbuf
);
1605 retval
= krb5_rd_safe(context
,auth_context
,&inbuf
,&outbuf
,NULL
);
1607 send_error(context
, fd
, retval
, gettext(
1608 "while decoding database size"));
1609 krb5_free_data_contents(context
, &inbuf
);
1610 com_err(progname
, retval
,
1611 gettext("while decoding database size from client"));
1614 memcpy((char *) &database_size
, outbuf
.data
, sizeof(database_size
));
1615 krb5_free_data_contents(context
, &inbuf
);
1616 krb5_free_data_contents(context
, &outbuf
);
1617 database_size
= ntohl(database_size
);
1620 * Initialize the initial vector.
1622 retval
= krb5_auth_con_initivector(context
, auth_context
);
1624 send_error(context
, fd
, retval
, gettext(
1625 "failed while initializing i_vector"));
1626 com_err(progname
, retval
, gettext("while initializing i_vector"));
1631 * Now start receiving the database from the net
1634 while (received_size
< database_size
) {
1635 retval
= krb5_read_message(context
, (void *) &fd
, &inbuf
);
1637 snprintf(buf
, sizeof (buf
),
1638 gettext("while reading database block starting at offset %d"),
1640 com_err(progname
, retval
, buf
);
1641 send_error(context
, fd
, retval
, buf
);
1644 if (krb5_is_krb_error(&inbuf
))
1645 recv_error(context
, &inbuf
);
1646 retval
= krb5_rd_priv(context
, auth_context
, &inbuf
,
1649 snprintf(buf
, sizeof (buf
),
1650 gettext("while decoding database block starting at offset %d"),
1652 com_err(progname
, retval
, buf
);
1653 send_error(context
, fd
, retval
, buf
);
1654 krb5_free_data_contents(context
, &inbuf
);
1657 n
= write(database_fd
, outbuf
.data
, outbuf
.length
);
1659 snprintf(buf
, sizeof (buf
),
1661 "while writing database block starting at offset %d"),
1663 send_error(context
, fd
, errno
, buf
);
1664 } else if (n
!= outbuf
.length
) {
1665 snprintf(buf
, sizeof (buf
),
1667 "incomplete write while writing database block starting at\n"
1668 "offset %d (%d written, %d expected)"),
1669 received_size
, n
, outbuf
.length
);
1670 send_error(context
, fd
, KRB5KRB_ERR_GENERIC
, buf
);
1672 received_size
+= outbuf
.length
;
1673 /* SUNWresync121: our krb5...contents sets length to 0 */
1674 krb5_free_data_contents(context
, &inbuf
);
1675 krb5_free_data_contents(context
, &outbuf
);
1679 * OK, we've seen the entire file. Did we get too many bytes?
1681 if (received_size
> database_size
) {
1682 snprintf(buf
, sizeof (buf
),
1683 gettext("Received %d bytes, expected %d bytes for database file"),
1684 received_size
, database_size
);
1685 send_error(context
, fd
, KRB5KRB_ERR_GENERIC
, buf
);
1688 * Create message acknowledging number of bytes received, but
1689 * don't send it until kdb5_util returns successfully.
1691 database_size
= htonl(database_size
);
1692 inbuf
.data
= (char *) &database_size
;
1693 inbuf
.length
= sizeof(database_size
);
1694 retval
= krb5_mk_safe(context
,auth_context
,&inbuf
,confmsg
,NULL
);
1696 com_err(progname
, retval
,
1697 gettext("while encoding # of receieved bytes"));
1698 send_error(context
, fd
, retval
,
1699 gettext("while encoding # of received bytes"));
1706 send_error(context
, fd
, err_code
, err_text
)
1707 krb5_context context
;
1709 krb5_error_code err_code
;
1717 memset((char *)&error
, 0, sizeof(error
));
1718 krb5_us_timeofday(context
, &error
.stime
, &error
.susec
);
1719 error
.server
= server
;
1720 error
.client
= client
;
1725 text
= error_message(err_code
);
1727 error
.error
= err_code
- ERROR_TABLE_BASE_krb5
;
1728 if (error
.error
> 127) {
1729 error
.error
= KRB_ERR_GENERIC
;
1731 sprintf(buf
, "%s %s", error_message(err_code
),
1736 error
.text
.length
= strlen(text
) + 1;
1737 error
.text
.data
= malloc(error
.text
.length
);
1738 if (error
.text
.data
) {
1739 strcpy(error
.text
.data
, text
);
1740 if (!krb5_mk_error(context
, &error
, &outbuf
)) {
1741 (void) krb5_write_message(context
, (void *)&fd
,&outbuf
);
1742 krb5_free_data_contents(context
, &outbuf
);
1744 free(error
.text
.data
);
1749 recv_error(context
, inbuf
)
1750 krb5_context context
;
1754 krb5_error_code retval
;
1756 retval
= krb5_rd_error(context
, inbuf
, &error
);
1758 com_err(progname
, retval
,
1759 gettext("while decoding error packet from client"));
1762 if (error
->error
== KRB_ERR_GENERIC
) {
1763 if (error
->text
.data
)
1765 gettext("Generic remote error: %s\n"),
1767 } else if (error
->error
) {
1768 com_err(progname
, error
->error
+ ERROR_TABLE_BASE_krb5
,
1769 gettext("signaled from server"));
1770 if (error
->text
.data
)
1772 gettext("Error text from client: %s\n"),
1775 krb5_free_error(context
, error
);
1780 load_database(context
, kdb_util
, database_file_name
)
1781 krb5_context context
;
1783 char *database_file_name
;
1785 static char *edit_av
[10];
1786 int error_ret
, save_stderr
= -1;
1790 /* <sys/param.h> has been included, so BSD will be defined on
1792 #if BSD > 0 && BSD <= 43
1794 #define WEXITSTATUS(w) (w).w_retcode
1800 krb5_error_code retval
;
1801 kdb_log_context
*log_ctx
;
1804 printf(gettext("calling kdb_util to load database\n"));
1806 log_ctx
= context
->kdblog_context
;
1808 edit_av
[0] = kdb_util
;
1811 edit_av
[count
++] = "-r";
1812 edit_av
[count
++] = realm
;
1814 edit_av
[count
++] = "load";
1815 if (kerb_database
) {
1816 edit_av
[count
++] = "-d";
1817 edit_av
[count
++] = kerb_database
;
1820 if (log_ctx
&& (log_ctx
->iproprole
== IPROP_SLAVE
)) {
1821 edit_av
[count
++] = "-i";
1823 edit_av
[count
++] = database_file_name
;
1824 edit_av
[count
++] = NULL
;
1826 switch(child_pid
= fork()) {
1828 com_err(progname
, errno
, gettext("while trying to fork %s"),
1834 save_stderr
= dup(2);
1838 open("/dev/null", O_RDWR
);
1843 execv(kdb_util
, edit_av
);
1846 dup2(save_stderr
, 2);
1847 com_err(progname
, retval
, gettext("while trying to exec %s"),
1853 printf(gettext("Child PID is %d\n"), child_pid
);
1854 if (wait(&waitb
) < 0) {
1855 com_err(progname
, errno
, gettext("while waiting for %s"),
1861 error_ret
= WEXITSTATUS(waitb
);
1863 com_err(progname
, 0,
1864 gettext("%s returned a bad exit status (%d)"),
1865 kdb_util
, error_ret
);