2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
8 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
10 * Openvision retains the copyright to derivative works of
11 * this source code. Do *NOT* create a derivative of this
12 * source code before consulting with your legal department.
13 * Do *NOT* integrate *ANY* of this source code into another
14 * product before consulting with your legal department.
16 * For further information, read the top-level Openvision
17 * copyright which is contained in the top-level MIT Kerberos
20 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
25 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
30 * Copyright (C) 1998 by the FundsXpress, INC.
32 * All rights reserved.
34 * Export of this software from the United States of America may require
35 * a specific license from the United States Government. It is the
36 * responsibility of any person or organization contemplating export to
37 * 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 FundsXpress. not be used in advertising or publicity pertaining
45 * to distribution of the software without specific, written prior
46 * permission. FundsXpress makes no representations about the suitability of
47 * this software for any purpose. It is provided "as is" without express
48 * or implied warranty.
50 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
51 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
52 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
58 * Beware future resyncers, this file is much diff from MIT (1.0...)
62 #include <stdio_ext.h>
65 #include <sys/types.h>
67 #include <sys/select.h>
70 #include <sys/socket.h>
72 #include <netinet/in.h>
73 #include <arpa/inet.h> /* inet_ntoa */
74 #include <gssapi/gssapi.h>
75 #include "gssapiP_krb5.h" /* for kg_get_context */
77 #include <kadm5/admin.h>
78 #include <kadm5/kadm_rpc.h>
79 #include <server_acl.h>
80 #include <krb5/adm_proto.h>
81 #include "kdb_kt.h" /* for krb5_ktkdb_set_context */
83 #include <kadm5/server_internal.h>
84 #include <gssapi_krb5.h>
87 #include <sys/resource.h>
88 #include <kdb/kdb_log.h>
91 #include <rpc/rpcsec_gss.h>
97 int signal_pure_report
= 0;
98 int signal_pure_clear
= 0;
99 void request_pure_report(int);
100 void request_pure_clear(int);
105 #define FD_SETSIZE 256
109 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
112 #if defined(NEED_DAEMON_PROTO)
113 extern int daemon(int, int);
116 static int signal_request_exit
= 0;
117 kadm5_config_params chgpw_params
;
118 void setup_signal_handlers(iprop_role iproprole
);
119 void request_exit(int);
121 void kadm_svc_run(void);
124 static struct sigaction s_action
;
125 #endif /* POSIX_SIGNALS */
130 typedef struct _auth_gssapi_name
{
135 gss_name_t gss_changepw_name
= NULL
, gss_oldchangepw_name
= NULL
;
136 void *global_server_handle
;
139 * This is a kludge, but the server needs these constants to be
140 * compatible with old clients. They are defined in <kadm5/admin.h>,
141 * but only if USE_KADM5_API_VERSION == 1.
143 #define OVSEC_KADM_ADMIN_SERVICE_P "ovsec_adm@admin"
144 #define OVSEC_KADM_CHANGEPW_SERVICE_P "ovsec_adm@changepw"
147 extern void krb5_iprop_prog_1();
148 extern kadm5_ret_t
kiprop_get_adm_host_srv_name(
156 in_port_t l_port
= 0; /* global local port num, for BSM audits */
158 int nofork
= 0; /* global; don't fork (debug mode) */
164 * Purpose: print out the server usage message
174 fprintf(stderr
, gettext("Usage: kadmind [-x db_args]* [-r realm] [-m] [-d] "
175 "[-p port-number]\n"));
180 * Function: display_status
182 * Purpose: displays GSS-API messages
186 * msg a string to be displayed with the message
187 * maj_stat the GSS-API major status code
188 * min_stat the GSS-API minor status code
192 * The GSS-API messages associated with maj_stat and min_stat are
193 * displayed on stderr, each preceeded by "GSS-API error <msg>: " and
194 * followed by a newline.
196 static void display_status_1(char *, OM_uint32
, int);
198 static void display_status(msg
, maj_stat
, min_stat
)
203 display_status_1(msg
, maj_stat
, GSS_C_GSS_CODE
);
204 display_status_1(msg
, min_stat
, GSS_C_MECH_CODE
);
207 static void display_status_1(m
, code
, type
)
212 OM_uint32 maj_stat
, min_stat
;
218 maj_stat
= gss_display_status(&min_stat
, code
,
219 type
, GSS_C_NULL_OID
,
221 fprintf(stderr
, "GSS-API error %s: %s\n", m
,
223 (void) gss_release_buffer(&min_stat
, &msg
);
232 * Solaris Kerberos: the following prototypes are needed because these are
233 * private interfaces that do not have prototypes in any .h
236 extern struct hostent
*res_getipnodebyaddr(const void *, size_t, int, int *);
237 extern void res_freehostent(struct hostent
*);
240 freedomnames(char **npp
)
254 * Construct a list of uniq FQDNs of all the net interfaces (except
255 * krb5.conf master dups) and return it in arg 'dnames'.
257 * On successful return (0), caller must call freedomnames()
261 getdomnames(krb5_context ctx
, char *realm
, char ***dnames
)
263 krb5_address
**addresses
= NULL
;
264 krb5_address
*a
= NULL
;
265 struct hostent
*hp
= NULL
;
266 int ret
, i
, result
=0, error
;
267 char **npp
= NULL
, **tpp
=NULL
;
269 char *cfhost
= NULL
; /* krb5 conf file master hostname */
271 if (ret
= kadm5_get_master(ctx
, realm
, &cfhost
)) {
275 ret
= krb5_os_localaddr(ctx
, &addresses
);
278 (void) fprintf(stderr
,
279 "kadmind: get localaddrs failed: %s",
286 for (i
=0; addresses
[i
]; i
++) {
288 hp
= res_getipnodebyaddr(a
->contents
, a
->length
,
289 a
->addrtype
== ADDRTYPE_INET
290 ? AF_INET
: AF_INET6
,
294 /* skip master host in krb5.conf */
295 if (strcasecmp(cfhost
, hp
->h_name
) == 0) {
303 /* skip if hostname already exists in list */
304 while (tpp
&& *tpp
++) {
305 if (strcasecmp(*(tpp
-1), hp
->h_name
) == 0) {
317 npp
= realloc(npp
, sizeof(char *) * (n
+ 2));
322 npp
[n
] = strdup(hp
->h_name
);
338 printf("getdomnames: n=%d, i=%d, npp=%p\n", n
, i
, npp
);
340 while (tpp
&& *tpp
++) {
341 printf("tpp=%s\n", *(tpp
-1));
364 krb5_free_addresses(ctx
, addresses
);
375 * Set the rpcsec_gss svc names for all net interfaces.
378 set_svc_domnames(char *svcname
, char **dnames
,
379 u_int program
, u_int version
)
388 /* MAX_NAME_LEN from rpc/rpcsec_gss.h */
389 char name
[MAXHOSTNAMELEN
+MAX_NAME_LEN
+2] = {0};
390 (void) snprintf(name
, sizeof(name
), "%s@%s",
392 ret
= rpc_gss_set_svc_name(name
,
396 (void) fprintf(stderr
,
397 "rpc_gss_set_svc_name success: %s\n",
402 /* XXX yuck. the signal handlers need this */
403 static krb5_context context
;
405 static krb5_context hctx
;
407 int main(int argc
, char *argv
[])
409 register SVCXPRT
*transp
;
411 extern int optind
, opterr
;
412 int ret
, rlen
, oldnames
= 0;
413 OM_uint32 OMret
, major_status
, minor_status
;
416 gss_buffer_desc in_buf
;
418 struct sockaddr_in addr
;
419 struct sockaddr_in
*sin
;
421 auth_gssapi_name names
[6];
422 gss_buffer_desc gssbuf
;
423 gss_OID nt_krb5_name_oid
;
425 struct netconfig
*nconf
;
429 struct t_bind tbindstr
, *tres
;
431 struct t_optmgmt req
, resp
;
436 char *kiprop_name
= NULL
; /* IProp svc name */
437 kdb_log_context
*log_ctx
;
438 kadm5_server_handle_t handle
;
441 kadm5_config_params params
;
442 char **db_args
= NULL
;
443 int db_args_size
= 0;
445 char **dnames
= NULL
;
449 /* Solaris Kerberos: Stores additional error messages */
452 /* Solaris Kerberos: Indicates whether loalhost is master or not */
453 krb5_boolean is_master
;
455 /* Solaris Kerberos: Used for checking acl file */
458 /* This is OID value the Krb5_Name NameType */
459 gssbuf
.value
= "{1 2 840 113554 1 2 2 1}";
460 gssbuf
.length
= strlen(gssbuf
.value
);
461 major_status
= gss_str_to_oid(&minor_status
, &gssbuf
, &nt_krb5_name_oid
);
462 if (major_status
!= GSS_S_COMPLETE
) {
464 gettext("Couldn't create KRB5 Name NameType OID\n"));
465 display_status("str_to_oid", major_status
, minor_status
);
469 names
[0].name
= names
[1].name
= names
[2].name
= names
[3].name
= NULL
;
470 names
[4].name
= names
[5].name
=NULL
;
471 names
[0].type
= names
[1].type
= names
[2].type
= names
[3].type
=
472 (gss_OID
) nt_krb5_name_oid
;
473 names
[4].type
= names
[5].type
= (gss_OID
) nt_krb5_name_oid
;
476 purify_start_batch();
478 whoami
= (strrchr(argv
[0], '/') ? strrchr(argv
[0], '/')+1 : argv
[0]);
480 (void) setlocale(LC_ALL
, "");
482 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
483 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
486 (void) textdomain(TEXT_DOMAIN
);
490 memset((char *) ¶ms
, 0, sizeof(params
));
492 while ((optchar
= getopt(argc
, argv
, "r:mdp:x:")) != EOF
) {
497 params
.realm
= optarg
;
498 params
.mask
|= KADM5_CONFIG_REALM
;
501 params
.mkey_from_kbd
= 1;
502 params
.mask
|= KADM5_CONFIG_MKEY_FROM_KBD
;
510 params
.kadmind_port
= atoi(optarg
);
511 params
.mask
|= KADM5_CONFIG_KADMIND_PORT
;
518 char **temp
= realloc( db_args
,
519 sizeof(char*) * (db_args_size
+1)); /* one for NULL */
522 fprintf(stderr
, gettext("%s: cannot initialize. Not enough memory\n"),
528 db_args
[db_args_size
-1] = optarg
;
529 db_args
[db_args_size
] = NULL
;
538 if (getrlimit(RLIMIT_NOFILE
, &rl
) == 0) {
539 rl
.rlim_cur
= rl
.rlim_max
= MAX(rl
.rlim_max
, FD_SETSIZE
);
540 (void) setrlimit(RLIMIT_NOFILE
, &rl
);
541 (void) enable_extended_FILE_stdio(-1, -1);
544 if ((ret
= kadm5_init_krb5_context(&context
))) {
546 gettext("%s: %s while initializing context, aborting\n"),
547 whoami
, error_message(ret
));
551 krb5_klog_init(context
, "admin_server", whoami
, 1);
553 /* Solaris Kerberos */
554 if((ret
= kadm5_init2("kadmind", NULL
,
556 KADM5_STRUCT_VERSION
,
559 &global_server_handle
,
560 &emsg
)) != KADM5_OK
) {
561 krb5_klog_syslog(LOG_ERR
,
562 gettext("%s while initializing, aborting"),
563 (emsg
? emsg
: error_message(ret
)));
565 gettext("%s: %s while initializing, aborting\n"),
566 whoami
, (emsg
? emsg
: error_message(ret
)));
569 krb5_klog_close(context
);
575 free(db_args
), db_args
=NULL
;
578 if ((ret
= kadm5_get_config_params(context
, 1, ¶ms
,
580 const char *e_txt
= krb5_get_error_message (context
, ret
);
581 /* Solaris Kerberos: Remove double "whoami" */
582 krb5_klog_syslog(LOG_ERR
, gettext("%s while initializing, aborting"),
585 gettext("%s: %s while initializing, aborting\n"),
587 kadm5_destroy(global_server_handle
);
588 krb5_klog_close(context
);
592 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE)
594 if ((params
.mask
& REQUIRED_PARAMS
) != REQUIRED_PARAMS
) {
595 /* Solaris Kerberos: Keep error messages consistent */
596 krb5_klog_syslog(LOG_ERR
,
597 gettext("Missing required configuration values (%lx)"
598 "while initializing, aborting"),
599 (params
.mask
& REQUIRED_PARAMS
) ^ REQUIRED_PARAMS
);
601 gettext("%s: Missing required configuration values "
602 "(%lx) while initializing, aborting\n"), whoami
,
603 (params
.mask
& REQUIRED_PARAMS
) ^ REQUIRED_PARAMS
);
604 krb5_klog_close(context
);
605 kadm5_destroy(global_server_handle
);
610 * When using the Horowitz/IETF protocol for
611 * password changing, the default port is 464
612 * (officially recognized by IANA)
614 * DEFAULT_KPASSWD_PORT -> 464
616 chgpw_params
.kpasswd_port
= DEFAULT_KPASSWD_PORT
;
617 chgpw_params
.mask
|= KADM5_CONFIG_KPASSWD_PORT
;
618 chgpw_params
.kpasswd_protocol
= KRB5_CHGPWD_CHANGEPW_V2
;
619 chgpw_params
.mask
|= KADM5_CONFIG_KPASSWD_PROTOCOL
;
621 if (ret
= kadm5_get_config_params(context
, 1, &chgpw_params
,
623 /* Solaris Kerberos: Remove double "whoami" */
624 krb5_klog_syslog(LOG_ERR
, gettext("%s while initializing,"
625 " aborting"), error_message(ret
));
627 gettext("%s: %s while initializing, aborting\n"),
628 whoami
, error_message(ret
));
629 krb5_klog_close(context
);
634 * We now setup the socket and bind() to port 464, so that
635 * kadmind can now listen to and process change-pwd requests
636 * from non-Solaris Kerberos V5 clients such as Microsoft,
639 if ((schpw
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
640 const char *e_txt
= krb5_get_error_message (context
, ret
);
641 krb5_klog_syslog(LOG_ERR
,
642 gettext( "Cannot create simple " "chpw socket: %s"),
644 fprintf(stderr
, gettext("Cannot create simple chpw socket: %s"),
646 kadm5_destroy(global_server_handle
);
647 krb5_klog_close(context
);
651 /* Solaris Kerberos: Ensure that kadmind is only run on a master kdc */
652 if (ret
= kadm5_is_master(context
, params
.realm
, &is_master
)){
653 krb5_klog_syslog(LOG_ERR
,
654 gettext("Failed to determine whether host is master "
655 "KDC for realm %s: %s"), params
.realm
,
658 gettext("%s: Failed to determine whether host is master "
659 "KDC for realm %s: %s\n"), whoami
, params
.realm
,
661 krb5_klog_close(context
);
665 if (is_master
== FALSE
) {
667 kadm5_get_master(context
, params
.realm
, &master
);
669 krb5_klog_syslog(LOG_ERR
,
670 gettext("%s can only be run on the master KDC, %s, for "
671 "realm %s"), whoami
, master
? master
: "unknown",
674 gettext("%s: %s can only be run on the master KDC, %s, for "
675 "realm %s\n"), whoami
, whoami
, master
? master
: "unknown",
677 krb5_klog_close(context
);
681 memset((char *) &addr
, 0, sizeof (struct sockaddr_in
));
682 addr
.sin_family
= AF_INET
;
683 addr
.sin_addr
.s_addr
= INADDR_ANY
;
684 l_port
= addr
.sin_port
= htons(params
.kadmind_port
);
687 if ((handlep
= setnetconfig()) == (void *) NULL
) {
688 (void) krb5_klog_syslog(LOG_ERR
,
689 gettext("cannot get any transport information"));
690 krb5_klog_close(context
);
694 while (nconf
= getnetconfig(handlep
)) {
695 if ((nconf
->nc_semantics
== NC_TPI_COTS_ORD
) &&
696 (strcmp(nconf
->nc_protofmly
, NC_INET
) == 0) &&
697 (strcmp(nconf
->nc_proto
, NC_TCP
) == 0))
701 if (nconf
== (struct netconfig
*) NULL
) {
702 (void) endnetconfig(handlep
);
703 krb5_klog_close(context
);
706 fd
= t_open(nconf
->nc_device
, O_RDWR
, &tinfo
);
708 krb5_klog_syslog(LOG_ERR
,
709 gettext("unable to open connection for ADMIN server"));
710 krb5_klog_close(context
);
714 opt
= (struct opthdr
*) reqbuf
;
715 opt
->level
= SOL_SOCKET
;
716 opt
->name
= SO_REUSEADDR
;
717 opt
->len
= sizeof (int);
720 * The option value is "1". This will allow the server to restart
721 * whilst the previous process is cleaning up after itself in a
722 * FIN_WAIT_2 or TIME_WAIT state. If another process is started
723 * outside of smf(5) then bind will fail anyway, which is what we want.
725 reqbuf
[sizeof (struct opthdr
)] = 1;
727 req
.flags
= T_NEGOTIATE
;
728 req
.opt
.len
= sizeof (struct opthdr
) + opt
->len
;
729 req
.opt
.buf
= (char *) opt
;
732 resp
.opt
.buf
= reqbuf
;
733 resp
.opt
.maxlen
= sizeof (reqbuf
);
735 if (t_optmgmt(fd
, &req
, &resp
) < 0 || resp
.flags
!= T_SUCCESS
) {
736 t_error("t_optmgmt");
739 /* Transform addr to netbuf */
741 tres
= (struct t_bind
*) t_alloc(fd
, T_BIND
, T_ADDR
);
744 (void) krb5_klog_syslog(LOG_ERR
,
745 gettext("cannot allocate netbuf"));
746 krb5_klog_close(context
);
750 tbindstr
.addr
.buf
= (char *) sin
;
751 tbindstr
.addr
.len
= tbindstr
.addr
.maxlen
= __rpc_get_a_size(tinfo
.addr
);
752 sin
= (struct sockaddr_in
*) tbindstr
.addr
.buf
;
753 /* SUNWresync121 XXX (void) memset(&addr, 0, sizeof(addr)); */
755 if (t_bind(fd
, &tbindstr
, tres
) < 0) {
757 const char *e_txt
= krb5_get_error_message (context
, errno
);
758 fprintf(stderr
, gettext("%s: Cannot bind socket.\n"), whoami
);
759 fprintf(stderr
, gettext("bind: %s\n"), e_txt
);
761 krb5_klog_syslog(LOG_ERR
, gettext("Cannot bind socket: %s"), e_txt
);
762 if(oerrno
== EADDRINUSE
) {
763 char *w
= strrchr(whoami
, '/');
770 fprintf(stderr
, gettext(
771 "This probably means that another %s process is already\n"
772 "running, or that another program is using the server port (number %d)\n"
773 "after being assigned it by the RPC portmap daemon. If another\n"
774 "%s is already running, you should kill it before\n"
775 "restarting the server. If, on the other hand, another program is\n"
776 "using the server port, you should kill it before running\n"
777 "%s, and ensure that the conflict does not occur in the\n"
778 "future by making sure that %s is started on reboot\n"
779 "before portmap.\n"), w
, ntohs(addr
.sin_port
), w
, w
, w
);
780 krb5_klog_syslog(LOG_ERR
, gettext("Check for already-running %s or for "
781 "another process using port %d"), w
,
782 htons(addr
.sin_port
));
784 kadm5_destroy(global_server_handle
);
785 krb5_klog_close(context
);
788 memset(&addr
, 0, sizeof(addr
));
789 addr
.sin_family
= AF_INET
;
790 addr
.sin_addr
.s_addr
= INADDR_ANY
;
792 addr
.sin_port
= htons(chgpw_params
.kpasswd_port
);
794 if (bind(schpw
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
797 const char *e_txt
= krb5_get_error_message (context
, errno
);
798 fprintf(stderr
, gettext("%s: Cannot bind socket.\n"), whoami
);
799 fprintf(stderr
, gettext("bind: %s\n"), e_txt
);
801 (void) snprintf(portbuf
, sizeof (portbuf
), "%d", ntohs(addr
.sin_port
));
802 krb5_klog_syslog(LOG_ERR
, gettext("cannot bind simple chpw socket: %s"),
804 if(oerrno
== EADDRINUSE
) {
805 char *w
= strrchr(whoami
, '/');
812 fprintf(stderr
, gettext(
813 "This probably means that another %s process is already\n"
814 "running, or that another program is using the server port (number %d).\n"
815 "If another %s is already running, you should kill it before\n"
816 "restarting the server.\n"),
817 w
, ntohs(addr
.sin_port
), w
);
819 krb5_klog_close(context
);
823 transp
= svc_tli_create(fd
, nconf
, NULL
, 0, 0);
824 (void) t_free((char *) tres
, T_BIND
);
825 (void) endnetconfig(handlep
);
827 fprintf(stderr
, gettext("%s: Cannot create RPC service.\n"), whoami
);
828 krb5_klog_syslog(LOG_ERR
, gettext("Cannot create RPC service: %m"));
829 kadm5_destroy(global_server_handle
);
830 krb5_klog_close(context
);
833 if(!svc_register(transp
, KADM
, KADMVERS
, kadm_1
, 0)) {
834 fprintf(stderr
, gettext("%s: Cannot register RPC service.\n"), whoami
);
835 krb5_klog_syslog(LOG_ERR
, gettext("Cannot register RPC service, failing."));
836 kadm5_destroy(global_server_handle
);
837 krb5_klog_close(context
);
843 * The only service principals which matter here are
844 * -> names[0].name (kadmin/<fqdn>)
845 * -> names[1].name (changepw/<fqdn>)
846 * KADM5_ADMIN_SERVICE_P, KADM5_CHANGEPW_SERVICE_P,
847 * OVSEC_KADM_ADMIN_SERVICE_P, OVSEC_KADM_CHANGEPW_SERVICE_P
848 * are all legacy service princs and calls to rpc_gss_set_svc_name()
849 * using these principals will always fail as they are not host
853 if (ret
= kadm5_get_adm_host_srv_name(context
, params
.realm
,
855 krb5_klog_syslog(LOG_ERR
,
856 gettext("Cannot get host based service name for admin "
857 "principal in realm %s: %s"), params
.realm
,
860 gettext("%s: Cannot get host based service name for admin "
861 "principal in realm %s: %s\n"), whoami
, params
.realm
,
863 krb5_klog_close(context
);
867 if (ret
= kadm5_get_cpw_host_srv_name(context
, params
.realm
,
869 krb5_klog_syslog(LOG_ERR
,
870 gettext("Cannot get host based service name for changepw "
871 "principal in realm %s: %s"), params
.realm
,
874 gettext("%s: Cannot get host based service name for "
875 "changepw principal in realm %s: %s\n"), whoami
, params
.realm
,
877 krb5_klog_close(context
);
880 names
[2].name
= KADM5_ADMIN_SERVICE_P
;
881 names
[3].name
= KADM5_CHANGEPW_SERVICE_P
;
882 names
[4].name
= OVSEC_KADM_ADMIN_SERVICE_P
;
883 names
[5].name
= OVSEC_KADM_CHANGEPW_SERVICE_P
;
885 if (names
[0].name
== NULL
|| names
[1].name
== NULL
||
886 names
[2].name
== NULL
|| names
[3].name
== NULL
||
887 names
[4].name
== NULL
|| names
[5].name
== NULL
) {
888 krb5_klog_syslog(LOG_ERR
,
889 gettext("Cannot initialize GSS-API authentication, "
892 gettext("%s: Cannot initialize "
893 "GSS-API authentication.\n"),
895 krb5_klog_close(context
);
900 * Go through some contortions to point gssapi at a kdb keytab.
901 * This prevents kadmind from needing to use an actual file-based
904 /* XXX extract kadm5's krb5_context */
905 hctx
= ((kadm5_server_handle_t
)global_server_handle
)->context
;
906 /* Set ktkdb's internal krb5_context. */
907 ret
= krb5_ktkdb_set_context(hctx
);
909 krb5_klog_syslog(LOG_ERR
, "Can't set kdb keytab's internal context.");
912 /* Solaris Kerberos */
913 ret
= krb5_db_set_mkey(hctx
, &((kadm5_server_handle_t
)global_server_handle
)->master_keyblock
);
915 krb5_klog_syslog(LOG_ERR
, "Can't set master key for kdb keytab.");
918 ret
= krb5_kt_register(context
, &krb5_kt_kdb_ops
);
920 krb5_klog_syslog(LOG_ERR
, "Can't register kdb keytab.");
923 /* Tell gssapi about the kdb keytab. */
924 ret
= krb5_gss_register_acceptor_identity("KDB:");
926 krb5_klog_syslog(LOG_ERR
, "Can't register acceptor keytab.");
931 krb5_klog_syslog(LOG_ERR
, "%s", krb5_get_error_message (context
, ret
));
932 fprintf(stderr
, "%s: Can't set up keytab for RPC.\n", whoami
);
933 kadm5_destroy(global_server_handle
);
934 krb5_klog_close(context
);
939 * Try to acquire creds for the old OV services as well as the
940 * new names, but if that fails just fall back on the new names.
943 if (rpc_gss_set_svc_name(names
[5].name
,
944 "kerberos_v5", 0, KADM
, KADMVERS
) &&
945 rpc_gss_set_svc_name(names
[4].name
,
946 "kerberos_v5", 0, KADM
, KADMVERS
))
948 if (rpc_gss_set_svc_name(names
[3].name
,
949 "kerberos_v5", 0, KADM
, KADMVERS
))
951 if (rpc_gss_set_svc_name(names
[2].name
,
952 "kerberos_v5", 0, KADM
, KADMVERS
))
955 /* If rpc_gss_set_svc_name() fails for either kadmin/<fqdn> or
956 * for changepw/<fqdn> then try to determine if this is caused
957 * by a missing keytab file or entry. If so, log it and continue.
959 if (rpc_gss_set_svc_name(names
[0].name
,
960 "kerberos_v5", 0, KADM
, KADMVERS
))
963 if (rpc_gss_set_svc_name(names
[1].name
,
964 "kerberos_v5", 0, KADM
, KADMVERS
))
967 retdn
= getdomnames(context
, params
.realm
, &dnames
);
968 if (retdn
== 0 && dnames
) {
970 * Multi-homed KDCs sometimes may need to set svc names
971 * for multiple net interfaces so we set them for
972 * all interfaces just in case.
974 set_svc_domnames(KADM5_ADMIN_HOST_SERVICE
,
975 dnames
, KADM
, KADMVERS
);
976 set_svc_domnames(KADM5_CHANGEPW_HOST_SERVICE
,
977 dnames
, KADM
, KADMVERS
);
980 /* if set_names succeeded, this will too */
981 in_buf
.value
= names
[1].name
;
982 in_buf
.length
= strlen(names
[1].name
) + 1;
983 (void) gss_import_name(&OMret
, &in_buf
, (gss_OID
) nt_krb5_name_oid
,
986 in_buf
.value
= names
[3].name
;
987 in_buf
.length
= strlen(names
[3].name
) + 1;
988 (void) gss_import_name(&OMret
, &in_buf
, (gss_OID
) nt_krb5_name_oid
,
989 &gss_oldchangepw_name
);
992 if ((ret
= kadm5int_acl_init(context
, 0, params
.acl_file
))) {
993 errmsg
= krb5_get_error_message (context
, ret
);
994 krb5_klog_syslog(LOG_ERR
, gettext("Cannot initialize acl file: %s"),
996 fprintf(stderr
, gettext("%s: Cannot initialize acl file: %s\n"),
998 kadm5_destroy(global_server_handle
);
999 krb5_klog_close(context
);
1005 * Warn if the acl file contains an entry for a principal matching the
1006 * default (unconfigured) acl rule.
1008 gssbuf
.length
= strlen("x/admin@___default_realm___");
1009 gssbuf
.value
= "x/admin@___default_realm___";
1010 /* Use any value as the first component - 'x' in this case */
1011 if (gss_import_name(&minor_status
, &gssbuf
, GSS_C_NT_USER_NAME
, &name
)
1012 == GSS_S_COMPLETE
) {
1013 if (kadm5int_acl_check(context
, name
, ACL_MODIFY
, NULL
, NULL
)) {
1014 krb5_klog_syslog(LOG_WARNING
,
1015 gettext("acls may not be properly configured: "
1016 "found an acl matching \"___default_realm___\" in "
1017 " %s"), params
.acl_file
);
1018 (void) fprintf(stderr
, gettext("%s: Warning: "
1019 "acls may not be properly configured: found an acl "
1020 "matching \"___default_realm___\" in %s\n"),
1021 whoami
, params
.acl_file
);
1023 (void) gss_release_name(&minor_status
, &name
);
1025 gssbuf
.value
= NULL
;
1030 * List the logs (FILE, STDERR, etc) which are currently being
1031 * logged to and print to stderr. Useful when trying to
1032 * track down a failure via SMF.
1034 if (ret
= krb5_klog_list_logs(whoami
)) {
1035 fprintf(stderr
, gettext("%s: %s while listing logs\n"),
1036 whoami
, error_message(ret
));
1037 krb5_klog_syslog(LOG_ERR
, gettext("%s while listing logs"),
1038 error_message(ret
));
1041 if (!nofork
&& (ret
= daemon(0, 0))) {
1043 errmsg
= krb5_get_error_message (context
, ret
);
1044 krb5_klog_syslog(LOG_ERR
,
1045 gettext("Cannot detach from tty: %s"), errmsg
);
1046 fprintf(stderr
, gettext("%s: Cannot detach from tty: %s\n"),
1048 kadm5_destroy(global_server_handle
);
1049 krb5_klog_close(context
);
1055 krb5_klog_syslog(LOG_INFO
, "Seeding random number generator");
1056 ret
= krb5_c_random_os_entropy(context
, 1, NULL
);
1058 krb5_klog_syslog(LOG_ERR
, "Error getting random seed: %s, aborting",
1059 krb5_get_error_message(context
, ret
));
1060 kadm5_destroy(global_server_handle
);
1061 krb5_klog_close(context
);
1067 handle
= global_server_handle
;
1068 ctx
= handle
->context
;
1069 if (params
.iprop_enabled
== TRUE
) {
1070 if (ret
= krb5_db_supports_iprop(ctx
, &iprop_supported
)) {
1072 gettext("%s: %s while trying to determine if KDB "
1073 "plugin supports iprop\n"), whoami
,
1074 error_message(ret
));
1075 krb5_klog_syslog(LOG_ERR
,
1076 gettext("%s while trying to determine if KDB "
1077 "plugin supports iprop"), error_message(ret
));
1078 krb5_klog_close(ctx
);
1082 if (!iprop_supported
) {
1084 gettext("%s: Warning, current KDB "
1085 "plugin does not support iprop, continuing "
1086 "with iprop disabled\n"), whoami
);
1087 krb5_klog_syslog(LOG_WARNING
,
1088 gettext("Warning, current KDB "
1089 "plugin does not support iprop, continuing "
1090 "with iprop disabled"));
1092 ulog_set_role(ctx
, IPROP_NULL
);
1094 ulog_set_role(ctx
, IPROP_MASTER
);
1096 ulog_set_role(ctx
, IPROP_NULL
);
1098 log_ctx
= ctx
->kdblog_context
;
1100 if (log_ctx
&& (log_ctx
->iproprole
== IPROP_MASTER
)) {
1102 * IProp is enabled, so let's map in the update log
1103 * and setup the service.
1105 if (ret
= ulog_map(ctx
, ¶ms
, FKADMIND
)) {
1107 gettext("%s: %s while mapping update log "
1108 "(`%s.ulog')\n"), whoami
, error_message(ret
),
1110 krb5_klog_syslog(LOG_ERR
,
1111 gettext("%s while mapping update log "
1112 "(`%s.ulog')"), error_message(ret
),
1114 krb5_klog_close(ctx
);
1121 "%s: create IPROP svc (PROG=%d, VERS=%d)\n",
1122 whoami
, KRB5_IPROP_PROG
, KRB5_IPROP_VERS
);
1124 if (!svc_create(krb5_iprop_prog_1
,
1125 KRB5_IPROP_PROG
, KRB5_IPROP_VERS
,
1128 gettext("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"),
1130 KRB5_IPROP_PROG
, KRB5_IPROP_VERS
);
1131 krb5_klog_syslog(LOG_ERR
,
1132 gettext("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
1133 KRB5_IPROP_PROG
, KRB5_IPROP_VERS
);
1134 krb5_klog_close(ctx
);
1138 if (ret
= kiprop_get_adm_host_srv_name(ctx
,
1141 krb5_klog_syslog(LOG_ERR
,
1142 gettext("%s while getting IProp svc name, failing"),
1143 error_message(ret
));
1145 gettext("%s: %s while getting IProp svc name, failing\n"),
1146 whoami
, error_message(ret
));
1147 krb5_klog_close(ctx
);
1151 if (!rpc_gss_set_svc_name(kiprop_name
, "kerberos_v5", 0,
1152 KRB5_IPROP_PROG
, KRB5_IPROP_VERS
)) {
1153 rpc_gss_error_t err
;
1154 (void) rpc_gss_get_error(&err
);
1156 krb5_klog_syslog(LOG_ERR
,
1157 gettext("Unable to set RPCSEC_GSS service name (`%s'), failing."),
1158 kiprop_name
? kiprop_name
: "<null>");
1160 gettext("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"),
1162 kiprop_name
? kiprop_name
: "<null>");
1166 "%s: set svc name (rpcsec err=%d, sys err=%d)\n",
1176 if (retdn
== 0 && dnames
) {
1177 set_svc_domnames(KADM5_KIPROP_HOST_SERVICE
,
1179 KRB5_IPROP_PROG
, KRB5_IPROP_VERS
);
1184 /* rpc_gss_set_svc_name failed for both kadmin/<fqdn> and
1187 krb5_klog_syslog(LOG_ERR
,
1188 gettext("Unable to set RPCSEC_GSS service names "
1190 names
[0].name
, names
[1].name
);
1192 gettext("%s: Unable to set RPCSEC_GSS service names "
1195 names
[0].name
, names
[1].name
);
1196 krb5_klog_close(context
);
1202 freedomnames(dnames
);
1204 setup_signal_handlers(log_ctx
->iproprole
);
1205 krb5_klog_syslog(LOG_INFO
, gettext("starting"));
1207 fprintf(stderr
, "%s: starting...\n", whoami
);
1211 * We now call our own customized async event processing
1212 * function kadm_svc_run(), as opposed to svc_run() earlier,
1213 * since this enables kadmind to also listen-to/process
1214 * non-RPCSEC_GSS based change-pwd requests apart from the
1215 * regular, RPCSEC_GSS kpasswd requests from Solaris Krb5 clients.
1219 krb5_klog_syslog(LOG_INFO
, gettext("finished, exiting"));
1220 kadm5_destroy(global_server_handle
);
1222 krb5_klog_close(context
);
1227 * Function: kadm_svc_run
1229 * Purpose: modified version of sunrpc svc_run.
1230 * which closes the database every TIMEOUT seconds.
1238 void kadm_svc_run(void)
1240 struct pollfd
*rfd
= 0;
1241 struct timeval timeout
;
1246 while(signal_request_exit
== 0) {
1247 timeout
.tv_sec
= TIMEOUT
;
1248 timeout
.tv_usec
= 0;
1250 if (nfds
!= svc_max_pollfd
) {
1251 rfd
= realloc(rfd
, sizeof (pollfd_t
) * svc_max_pollfd
);
1252 nfds
= svc_max_pollfd
;
1255 (void) memcpy(rfd
, svc_pollfd
,
1256 sizeof (pollfd_t
) * svc_max_pollfd
);
1258 for (i
= 0; i
< nfds
; i
++) {
1259 if (rfd
[i
].fd
== -1) {
1261 rfd
[i
].events
= POLLIN
;
1266 switch(pollret
= poll(rfd
, nfds
,
1267 __rpc_timeval_to_msec(&timeout
))) {
1276 for (i
= 0; i
< nfds
; i
++) {
1277 if (rfd
[i
].revents
& POLLIN
) {
1278 if (rfd
[i
].fd
== schpw
)
1279 handle_chpw(context
, schpw
,
1280 global_server_handle
,
1283 svc_getreq_poll(rfd
, pollret
);
1286 if (i
== (nfds
- 1))
1297 * Function: setup_signal_handlers
1299 * Purpose: Setup signal handling functions with either
1300 * System V's signal() or POSIX_SIGNALS.
1302 void setup_signal_handlers(iprop_role iproprole
) {
1303 #ifdef POSIX_SIGNALS
1304 (void) sigemptyset(&s_action
.sa_mask
);
1305 s_action
.sa_handler
= request_exit
;
1306 (void) sigaction(SIGINT
, &s_action
, (struct sigaction
*) NULL
);
1307 (void) sigaction(SIGTERM
, &s_action
, (struct sigaction
*) NULL
);
1308 (void) sigaction(SIGQUIT
, &s_action
, (struct sigaction
*) NULL
);
1309 s_action
.sa_handler
= sig_pipe
;
1310 (void) sigaction(SIGPIPE
, &s_action
, (struct sigaction
*) NULL
);
1313 * IProp will fork for a full-resync, we don't want to
1314 * wait on it and we don't want the living dead procs either.
1316 if (iproprole
== IPROP_MASTER
) {
1317 s_action
.sa_handler
= SIG_IGN
;
1318 (void) sigaction(SIGCHLD
, &s_action
, (struct sigaction
*) NULL
);
1321 signal(SIGINT
, request_exit
);
1322 signal(SIGTERM
, request_exit
);
1323 signal(SIGQUIT
, request_exit
);
1324 signal(SIGPIPE
, sig_pipe
);
1327 * IProp will fork for a full-resync, we don't want to
1328 * wait on it and we don't want the living dead procs either.
1330 if (iproprole
== IPROP_MASTER
)
1331 (void) signal(SIGCHLD
, SIG_IGN
);
1333 #endif /* POSIX_SIGNALS */
1339 * Function: request_exit
1341 * Purpose: sets flags saying the server got a signal and that it
1342 * should exit when convient.
1347 * modifies signal_request_exit which ideally makes the server exit
1351 * signal_request_exit
1354 void request_exit(int signum
)
1356 krb5_klog_syslog(LOG_NOTICE
, gettext("Got signal to request exit"));
1357 signal_request_exit
= 1;
1362 * Function: sig_pipe
1364 * Purpose: SIGPIPE handler
1366 * Effects: krb5_klog_syslogs a message that a SIGPIPE occurred and returns,
1367 * thus causing the read() or write() to fail and, presumable, the RPC
1368 * to recover. Otherwise, the process aborts.
1370 void sig_pipe(int unused
)
1372 #ifndef POSIX_SIGNALS
1373 signal(SIGPIPE
, sig_pipe
);
1374 #endif /* POSIX_SIGNALS */
1375 krb5_klog_syslog(LOG_NOTICE
, gettext("Warning: Received a SIGPIPE; "
1376 "probably a client aborted. Continuing."));