From: Marc Horowitz Date: Thu, 25 Jul 1996 22:30:02 +0000 (+0000) Subject: * Makefile.in, configure.in, adm_rw.c, kadm5_defs.h, main.c, X-Git-Tag: krb5-1.0-beta7~257 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=bf9ec031942b16e4bff9bcb52878d1419554c058;p=krb5.git * Makefile.in, configure.in, adm_rw.c, kadm5_defs.h, main.c, proto_serv.c, srv_net.c: combined pieces of the old v5server and kadm libraries with calls to the new kadm5srv library to produce a daemon which handles the v5b6 "simple" password changing protocol. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8825 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/kadmin/v5passwdd/ChangeLog b/src/kadmin/v5passwdd/ChangeLog new file mode 100644 index 000000000..425781a3f --- /dev/null +++ b/src/kadmin/v5passwdd/ChangeLog @@ -0,0 +1,6 @@ +Thu Jul 25 17:01:32 1996 Marc Horowitz + + * Makefile.in, configure.in, adm_rw.c, kadm5_defs.h, main.c, + proto_serv.c, srv_net.c: combined pieces of the old v5server and + kadm libraries with calls to the new kadm5srv library to produce a + daemon which handles the v5b6 "simple" password changing protocol. diff --git a/src/kadmin/v5passwdd/Makefile.in b/src/kadmin/v5passwdd/Makefile.in new file mode 100644 index 000000000..a46a32686 --- /dev/null +++ b/src/kadmin/v5passwdd/Makefile.in @@ -0,0 +1,18 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +SRCS = main.c srv_net.c proto_serv.c adm_rw.c +OBJS = main.o srv_net.o proto_serv.o adm_rw.o + +PROG = v5passwdd + +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPLIBS) + $(LD) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(C)$(PROG) ${DESTDIR}$(SERVER_BINDIR)$(S)$(PROG) + +clean:: + $(RM) $(PROG) + diff --git a/src/kadmin/v5passwdd/adm_rw.c b/src/kadmin/v5passwdd/adm_rw.c new file mode 100644 index 000000000..bed4e4bb1 --- /dev/null +++ b/src/kadmin/v5passwdd/adm_rw.c @@ -0,0 +1,534 @@ +/* + * lib/kadm/adm_rw.c + * + * Copyright 1995 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +/* + * Routines to engage in the administrative (password changing) protocol. + */ +#define NEED_SOCKETS +#include "k5-int.h" +#include "adm_proto.h" + +/* + * Local prototypes (needed or else the PC will pass fail). + */ +static void kadm_copyin_int32 PROTOTYPE((char *, krb5_int32 *)); +static void kadm_copyout_int32 PROTOTYPE((krb5_int32, char *)); + +/* + * Routines to [de]serialize integers. + * + * kadm_copyin_int32 - Move a 32-bit integer fron network byte order to + * host byte order. + * kadm_copyout_int32 - Move a 32-bit integer from host byte order to + * network byte order. + */ +static void +kadm_copyin_int32(cp, ip) + char *cp; + krb5_int32 *ip; +{ + *ip = (((krb5_int32) ((unsigned char) cp[0]) << 24) + + ((krb5_int32) ((unsigned char) cp[1]) << 16) + + ((krb5_int32) ((unsigned char) cp[2]) << 8) + + ((krb5_int32) ((unsigned char) cp[3]))); +} + +static void +kadm_copyout_int32(outint, cp) + krb5_int32 outint; + char *cp; +{ + cp[0] = (char) ((outint >> 24) & 0xff); + cp[1] = (char) ((outint >> 16) & 0xff); + cp[2] = (char) ((outint >> 8) & 0xff); + cp[3] = (char) (outint & 0xff); +} + +/* + * krb5_free_adm_data() - Free data blocks allocated by read_adm... routines. + */ +void INTERFACE +krb5_free_adm_data(kcontext, ncomp, datap) + krb5_context kcontext; + krb5_int32 ncomp; + krb5_data *datap; +{ + int i; + + if (datap) { + for (i=0; i 0)) + krb5_xfree(datap[i].data); + + krb5_xfree(datap); + } +} + +/* + * krb5_send_adm_cmd() - Send an administrative command. + * + * Send a list of data in a KRB_PRIV message. Data takes the format: + * nargs (4 octets in network order) + * arg size 1 (4 octets in network order) + * arg data 1 ("arg size 1" octets) + * . + * . + * . + */ +krb5_error_code INTERFACE +krb5_send_adm_cmd(kcontext, sock, ctx, nargs, arglist) + krb5_context kcontext; /* Context handle (In ) */ + krb5_pointer sock; /* Socket to write to (In ) */ + krb5_auth_context ctx; /* Auth context (In ) */ + krb5_int32 nargs; /* Number of arguments (In ) */ + krb5_data *arglist; /* Components to write (In ) */ +{ + int writebufsize; + int i; + char *writebuf; + krb5_error_code ret; + krb5_int32 ac_flags; + + /* + * First check that our auth context has the right flags in it. + */ + if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags)) + return(ret); + + if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE| + KRB5_AUTH_CONTEXT_DO_SEQUENCE)) != + (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { + /* XXX - need a better error */ + return(KRB5KRB_AP_ERR_MSG_TYPE); + } + + ret = 0; + /* Calculate write buffer size */ + writebufsize = sizeof(krb5_int32); + for (i=0; i= sizeof(krb5_int32)) { + curr = msg_data.data; + kadm_copyin_int32(curr, nargs); + curr += sizeof(krb5_int32); + + /* Are there any components to copy? */ + if (*nargs > 0) { + + /* Get the memory for the list */ + if (*arglist = (krb5_data *) + malloc((size_t) (*nargs) * sizeof(krb5_data))) { + krb5_data *xarglist; + + xarglist = *arglist; + memset((char *) (xarglist), 0, + (size_t) (*nargs) * sizeof(krb5_data)); + + replyok = 1; + /* Copy out each list entry */ + for (i=0; i<*nargs; i++) { + + /* First get the length of the reply component */ + if (curr + sizeof(krb5_int32) - msg_data.data <= + msg_data.length) { + + kadm_copyin_int32(curr, &len32); + xarglist[i].length = (int) len32; + curr += sizeof(krb5_int32); + + /* Then get the memory for the actual data */ + if ((curr + xarglist[i].length - + msg_data.data <= msg_data.length) && + (xarglist[i].data = (char *) + malloc(xarglist[i].length+1))) { + + /* Then copy it out */ + memcpy(xarglist[i].data, + curr, + xarglist[i].length); + curr += xarglist[i].length; + + /* Null terminate for convenience */ + xarglist[i].data[xarglist[i].length] + = '\0'; + } + else { + /* Not enough remaining data. */ + replyok = 0; + break; + } + } + else { + /* Not enough remaining data */ + replyok = 0; + break; + } + } + if (!replyok) + krb5_free_adm_data(kcontext, *nargs, *arglist); + } + } + else { + if (*nargs == 0) { + *arglist = (krb5_data *) NULL; + replyok = 1; + } + } + } + if (!replyok) { + ret = KRB5KRB_AP_ERR_MSG_TYPE; /* syntax error */ + } + memset(msg_data.data, 0, msg_data.length); + krb5_xfree(msg_data.data); + } + krb5_xfree(read_data.data); + } + return(ret); +} + +/* + * krb5_read_adm_reply() - Read an administrative protocol response. + * + * Expect to read them out in the same format as send_adm_reply shoots them + * in. + * + * It is the caller's responsibility to free the memory allocated for + * the read in component list. + */ +krb5_error_code INTERFACE +krb5_read_adm_reply(kcontext, sock, ctx, cmd_stat, ncomps, complist) + krb5_context kcontext; /* Context handle (In ) */ + krb5_pointer sock; /* Socket to read from (In ) */ + krb5_auth_context ctx; /* Auth context (In ) */ + krb5_int32 *cmd_stat; /* Command status (Out) */ + krb5_int32 *ncomps; /* # of reply components(Out) */ + krb5_data **complist; /* List of components (Out) */ +{ + krb5_data read_data; + krb5_error_code ret; + krb5_data msg_data; + krb5_replay_data replay_data; + krb5_int32 ac_flags; + krb5_int32 len32; + + /* + * First check that our auth context has the right flags in it. + */ + if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags)) + return(ret); + + if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE| + KRB5_AUTH_CONTEXT_DO_SEQUENCE)) != + (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { + /* XXX - need a better error */ + return(KRB5KRB_AP_ERR_MSG_TYPE); + } + + if (!(ret = krb5_read_message(kcontext, sock, &read_data))) { + if (!(ret = krb5_rd_priv(kcontext, + ctx, + &read_data, + &msg_data, + &replay_data))) { + char *curr; + int replyok; + int i; + + replyok = 0; + /* We'd better have at least two reply components */ + if (msg_data.length >= (2*sizeof(krb5_int32))) { + curr = msg_data.data; + kadm_copyin_int32(curr, cmd_stat); + curr += sizeof(krb5_int32); + kadm_copyin_int32(curr, ncomps); + curr += sizeof(krb5_int32); + + /* Are there any components to copy? */ + if (*ncomps > 0) { + + /* Get the memory for the list */ + if (*complist = (krb5_data *) + malloc((size_t) ((*ncomps) * sizeof(krb5_data)))) { + krb5_data *xcomplist; + + xcomplist = *complist; + memset((char *) (xcomplist), 0, + (size_t) ((*ncomps) * sizeof(krb5_data))); + + replyok = 1; + /* Copy out each list entry */ + for (i=0; i<*ncomps; i++) { + + /* First get the length of the reply component */ + if (curr + sizeof(krb5_int32) - msg_data.data <= + msg_data.length) { + kadm_copyin_int32(curr, &len32); + xcomplist[i].length = (int) len32; + curr += sizeof(krb5_int32); + + /* Then get the memory for the actual data */ + if ((curr + xcomplist[i].length - + msg_data.data <= msg_data.length) && + (xcomplist[i].data = (char *) + malloc(xcomplist[i].length+1))) { + + /* Then copy it out */ + memcpy(xcomplist[i].data, + curr, + xcomplist[i].length); + curr += xcomplist[i].length; + + /* Null terminate for convenience */ + xcomplist[i].data[xcomplist[i].length] + = '\0'; + } + else { + /* Not enough remaining data. */ + replyok = 0; + break; + } + } + else { + /* Not enough remaining data */ + replyok = 0; + break; + } + } + if (!replyok) + krb5_free_adm_data(kcontext, *ncomps, *complist); + } + } + else { + if (*ncomps == 0) { + *complist = (krb5_data *) NULL; + replyok = 1; + } + } + } + if (!replyok) { + ret = KRB5KRB_AP_ERR_MSG_TYPE; /* syntax error */ + } + memset(msg_data.data, 0, msg_data.length); + krb5_xfree(msg_data.data); + } + krb5_xfree(read_data.data); + } + return(ret); +} diff --git a/src/kadmin/v5passwdd/configure.in b/src/kadmin/v5passwdd/configure.in new file mode 100644 index 000000000..ce43d2f2e --- /dev/null +++ b/src/kadmin/v5passwdd/configure.in @@ -0,0 +1,17 @@ +AC_INIT(proto_serv.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_FUNC_CHECK(waitpid,AC_DEFINE(HAVE_WAITPID)) +AC_FUNC_CHECK(vsprintf,AC_DEFINE(HAVE_VSPRINTF)) +AC_CHECK_HEADERS(sys/select.h) +CHECK_SIGNALS +CHECK_SETJMP +CHECK_WAIT_TYPE +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_KDB5_LIBRARY +USE_DYN_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/v5passwdd/kadm5_defs.h b/src/kadmin/v5passwdd/kadm5_defs.h new file mode 100644 index 000000000..67339bc15 --- /dev/null +++ b/src/kadmin/v5passwdd/kadm5_defs.h @@ -0,0 +1,363 @@ +/* + * kadmin/v5server/kadm5_defs.h + * + * Copyright 1995 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +/* + * kadmind5 + * Version 5 administrative daemon. + */ +#ifndef KADM5_DEFS_H__ +#define KADM5_DEFS_H__ + +/* + * Debug definitions. + */ +#define DEBUG_SPROC 1 +#define DEBUG_OPERATION 2 +#define DEBUG_HOST 4 +#define DEBUG_REALM 8 +#define DEBUG_REQUESTS 16 +#define DEBUG_ACL 32 +#define DEBUG_PROTO 64 +#define DEBUG_CALLS 128 +#define DEBUG_NOSLAVES 256 +#ifdef DEBUG +#define DPRINT(l1, cl, al) if ((cl & l1) != 0) xprintf al +#else /* DEBUG */ +#define DPRINT(l1, cl, al) +#endif /* DEBUG */ +#define DLOG(l1, cl, msg) if ((cl & l1) != 0) \ + com_err(programname, 0, msg) + +/* + * Access control bits. + */ +#define ACL_ADD_PRINCIPAL 1 +#define ACL_DELETE_PRINCIPAL 2 +#define ACL_MODIFY_PRINCIPAL 4 +#define ACL_CHANGEPW 8 +#define ACL_CHANGE_OWN_PW 16 +#define ACL_INQUIRE 32 +#define ACL_EXTRACT 64 +#define ACL_RENAME_PRINCIPAL (ACL_ADD_PRINCIPAL+ACL_DELETE_PRINCIPAL) + +#define ACL_PRINCIPAL_MASK (ACL_ADD_PRINCIPAL|ACL_DELETE_PRINCIPAL|\ + ACL_MODIFY_PRINCIPAL) +#define ACL_PASSWD_MASK (ACL_CHANGEPW|ACL_CHANGE_OWN_PW) +#define ACL_ALL_MASK (ACL_ADD_PRINCIPAL | \ + ACL_DELETE_PRINCIPAL | \ + ACL_MODIFY_PRINCIPAL | \ + ACL_CHANGEPW | \ + ACL_CHANGE_OWN_PW | \ + ACL_INQUIRE | \ + ACL_EXTRACT) +/* + * Subcodes. + */ +#define KADM_BAD_ARGS 10 +#define KADM_BAD_CMD 11 +#define KADM_NO_CMD 12 +#define KADM_BAD_PRINC 20 +#define KADM_PWD_TOO_SHORT 21 +#define KADM_PWD_WEAK 22 +#define KADM_NOT_ALLOWED 100 + +/* + * Reply status values. + */ +#define KRB5_ADM_SUCCESS 0 +#define KRB5_ADM_CMD_UNKNOWN 1 +#define KRB5_ADM_PW_UNACCEPT 2 +#define KRB5_ADM_BAD_PW 3 +#define KRB5_ADM_NOT_IN_TKT 4 +#define KRB5_ADM_CANT_CHANGE 5 +#define KRB5_ADM_LANG_NOT_SUPPORTED 6 + +#define KRB5_ADM_P_ALREADY_EXISTS 64 +#define KRB5_ADM_P_DOES_NOT_EXIST 65 +#define KRB5_ADM_NOT_AUTHORIZED 66 +#define KRB5_ADM_BAD_OPTION 67 +#define KRB5_ADM_VALUE_REQUIRED 68 +#define KRB5_ADM_SYSTEM_ERROR 69 +#define KRB5_ADM_KEY_DOES_NOT_EXIST 70 +#define KRB5_ADM_KEY_ALREADY_EXISTS 71 +#define KRB5_ADM_BAD_DESKEY 72 + +/* + * Inter-module function prototypes + */ + +krb5_keytab key_keytab_id(); +krb5_error_code key_open_db KRB5_PROTOTYPE((krb5_context)); +krb5_error_code key_close_db KRB5_PROTOTYPE((krb5_context)); + +#if 0 +/* srv_key.c */ +krb5_error_code key_init + KRB5_PROTOTYPE((krb5_context, + int, + int, + char *, + int, + char *, + char *, + char *, + char *, + krb5_int32, + krb5_key_salt_tuple *)); +void key_finish + KRB5_PROTOTYPE((krb5_context, + int)); +krb5_error_code key_string_to_keys + KRB5_PROTOTYPE((krb5_context, + krb5_db_entry *, + krb5_data *, + krb5_int32, + krb5_key_salt_tuple *, + krb5_int32 *, + krb5_key_data **)); +krb5_error_code key_random_key + KRB5_PROTOTYPE((krb5_context, + krb5_db_entry *, + krb5_int32 *, + krb5_key_data **)); +krb5_error_code key_encrypt_keys + KRB5_PROTOTYPE((krb5_context, + krb5_db_entry *, + krb5_int32 *, + krb5_key_data *, + krb5_key_data **)); +krb5_error_code key_decrypt_keys + KRB5_PROTOTYPE((krb5_context, + krb5_db_entry *, + krb5_int32 *, + krb5_key_data *, + krb5_key_data **)); +krb5_boolean key_pwd_is_weak + KRB5_PROTOTYPE((krb5_context, + krb5_db_entry *, + krb5_data *)); +krb5_db_entry *key_master_entry(); +char *key_master_realm(); +krb5_keyblock *key_admin_key(); +krb5_encrypt_block *key_master_encblock(); +void key_free_key_data KRB5_PROTOTYPE((krb5_key_data *, + krb5_int32)); +krb5_error_code key_dbent_to_keysalts + KRB5_PROTOTYPE((krb5_db_entry *, + krb5_int32 *, + krb5_key_salt_tuple **)); +krb5_error_code key_update_tl_attrs + KRB5_PROTOTYPE((krb5_context, + krb5_db_entry *, + krb5_principal, + krb5_boolean)); + +/* srv_acl.c */ +krb5_error_code acl_init + KRB5_PROTOTYPE((krb5_context, + int, + char *)); +void acl_finish + KRB5_PROTOTYPE((krb5_context, + int)); +krb5_boolean acl_op_permitted + KRB5_PROTOTYPE((krb5_context, + krb5_principal, + krb5_int32, + char *)); + +#endif + +/* srv_output.c */ +krb5_error_code output_init + KRB5_PROTOTYPE((krb5_context, + int, + char *, + krb5_boolean)); +void output_finish + KRB5_PROTOTYPE((krb5_context, + int)); +krb5_boolean output_lang_supported + KRB5_PROTOTYPE((char *)); +char *output_krb5_errmsg + KRB5_PROTOTYPE((char *, + krb5_boolean, + krb5_int32)); +char *output_adm_error + KRB5_PROTOTYPE((char *, + krb5_boolean, + krb5_int32, + krb5_int32, + krb5_int32, + krb5_data *)); + +/* srv_net.c */ +krb5_error_code net_init + KRB5_PROTOTYPE((krb5_context, + char *, + int, + krb5_int32)); +void net_finish + KRB5_PROTOTYPE((krb5_context, + int)); +krb5_error_code net_dispatch + KRB5_PROTOTYPE((krb5_context, int)); +krb5_principal net_server_princ(); + +/* proto_serv.c */ +krb5_error_code proto_init + KRB5_PROTOTYPE((krb5_context, + int, + int)); +void proto_finish + KRB5_PROTOTYPE((krb5_context, + int)); +krb5_error_code proto_serv + KRB5_PROTOTYPE((krb5_context, + krb5_int32, + int, + void *, + void *)); + +krb5_int32 passwd_change + KRB5_PROTOTYPE((krb5_context, + int, + krb5_auth_context, + krb5_ticket *, + krb5_data *, + krb5_data *, + krb5_int32 *)); + +krb5_int32 pwd_change + KRB5_PROTOTYPE((krb5_context, + int, + krb5_auth_context, + krb5_ticket *, + krb5_data *, + krb5_data *, + char [])); + +#if 0 + +/* passwd.c */ +krb5_int32 passwd_check + KRB5_PROTOTYPE((krb5_context, + int, + krb5_auth_context, + krb5_ticket *, + krb5_data *, + krb5_int32 *)); +krb5_int32 passwd_change + KRB5_PROTOTYPE((krb5_context, + int, + krb5_auth_context, + krb5_ticket *, + krb5_data *, + krb5_data *, + krb5_int32 *)); +krb5_boolean passwd_check_npass_ok + KRB5_PROTOTYPE((krb5_context, + int, + krb5_principal, + krb5_db_entry *, + krb5_data *, + krb5_int32 *)); +krb5_boolean passwd_check_opass_ok + KRB5_PROTOTYPE((krb5_context, + int, + krb5_principal, + krb5_db_entry *, + krb5_data *)); + +/* admin.c */ +krb5_error_code admin_add_principal + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_int32, + krb5_data *)); +krb5_error_code admin_delete_principal + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_data *)); +krb5_error_code admin_rename_principal + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_data *, + krb5_data *)); +krb5_error_code admin_modify_principal + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_int32, + krb5_data *)); +krb5_error_code admin_change_opw + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_data *, + krb5_data *)); +krb5_error_code admin_change_orandpw + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_data *)); +krb5_error_code admin_inquire + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_data *, + krb5_int32 *, + krb5_data **)); +krb5_error_code admin_extract_key + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_data *, + krb5_data *, + krb5_int32 *, + krb5_data **)); +krb5_error_code admin_add_key + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_int32, + krb5_data *)); +krb5_error_code admin_delete_key + KRB5_PROTOTYPE((krb5_context, + int, + krb5_ticket *, + krb5_int32, + krb5_data *)); +void admin_init KRB5_PROTOTYPE((krb5_deltat, + krb5_deltat, + krb5_boolean, + krb5_timestamp, + krb5_boolean, + krb5_flags)); +#endif + +#endif /* KADM5_DEFS_H__ */ diff --git a/src/kadmin/v5passwdd/main.c b/src/kadmin/v5passwdd/main.c new file mode 100644 index 000000000..a37e67bfc --- /dev/null +++ b/src/kadmin/v5passwdd/main.c @@ -0,0 +1,289 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "kadm5_defs.h" + +static krb5_keytab keytab; +char *programname; +kadm5_config_params params; +void *global_server_handle; +#if POSIX_SETJMP +static sigjmp_buf terminal_jmp; +#else /* POSIX_SETJMP */ +static jmp_buf terminal_jmp; +#endif /* POSIX_SETJMP */ + +krb5_keytab key_keytab_id() +{ + return(keytab); +} + +static krb5_sigtype +unhandled_signal(signo) + int signo; +{ +#if POSIX_SETJMP + siglongjmp(terminal_jmp, signo); +#else /* POSIX_SETJMP */ + longjmp(terminal_jmp, signo); +#endif /* POSIX_SETJMP */ + /* NOTREACHED */ +} + +void usage() +{ + fprintf(stderr, "Usage: kadmind [-r realm] [-m] [-nofork] " + "[-port port-number]\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int ret, rlen, nofork, oldnames = 0; + krb5_error_code code; + int debug_level = 0; +#if POSIX_SIGNALS + struct sigaction s_action; +#endif /* POSIX_SIGNALS */ + krb5_context context; + + programname = argv[0]; + + nofork = 0; + + memset((char *) ¶ms, 0, sizeof(params)); + + argc--; argv++; + while (argc) { + if (strcmp(*argv, "-r") == 0) { + argc--; argv++; + if (!argc) + usage(); + params.realm = *argv; + params.mask |= KADM5_CONFIG_REALM; + argc--; argv++; + continue; + } else if (strcmp(*argv, "-m") == 0) { + params.mkey_from_kbd = 1; + params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + } else if (strcmp(*argv, "-nofork") == 0) { + nofork = 1; + } else if(strcmp(*argv, "-port") == 0) { + argc--; argv++; + if(!argc) + usage(); + params.kadmind_port = atoi(*argv); + params.mask |= KADM5_CONFIG_KADMIND_PORT; + } else + break; + argc--; argv++; + } + + if (argc != 0) + usage(); + + if (ret = krb5_init_context(&context)) { + fprintf(stderr, "%s: %s while initializing context, aborting\n", + programname, error_message(ret)); + exit(1); + } + + krb5_klog_init(context, "admin_server", programname, 1); + + if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, + ¶ms)) { + krb5_klog_syslog(LOG_ERR, "%s: %s while initializing, aborting\n", + programname, error_message(ret)); + fprintf(stderr, "%s: %s while initializing, aborting\n", + programname, error_message(ret)); + krb5_klog_close(); + exit(1); + } + +#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE | \ + KADM5_CONFIG_ADMIN_KEYTAB) + + if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { + krb5_klog_syslog(LOG_ERR, "%s: Missing required configuration values " + "(%x) while initializing, aborting\n", programname, + (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); + fprintf(stderr, "%s: Missing required configuration values " + "(%x) while initializing, aborting\n", programname, + (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); + krb5_klog_close(); + exit(1); + } + + if ((code = krb5_kt_resolve(context, params.admin_keytab, &keytab))) { + fprintf(stderr, "%s: cannot resolve keytab %s (%s).\n", + programname, params.admin_keytab, error_message(code)); + exit(1); + } + + if (!nofork && + daemon(0, ((params.mask&KADM5_CONFIG_MKEY_FROM_KBD)? + params.mkey_from_kbd:0))) { + fprintf(stderr, "%s: cannot spawn and detach.\n", argv[0]); + perror(argv[0]); + return(2); + } + +#if POSIX_SIGNALS + (void) sigemptyset(&s_action.sa_mask); + s_action.sa_flags = 0; + s_action.sa_handler = unhandled_signal; + (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGALRM, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL); +#else /* POSIX_SIGNALS */ + signal(SIGINT, unhandled_signal); + signal(SIGTERM, unhandled_signal); + signal(SIGHUP, unhandled_signal); + signal(SIGQUIT, unhandled_signal); + signal(SIGPIPE, unhandled_signal); + signal(SIGALRM, unhandled_signal); + signal(SIGCHLD, unhandled_signal); +#endif /* POSIX_SIGNALS */ + + krb5_klog_syslog(LOG_INFO, "starting"); + + if (code = net_init(context, params.realm, debug_level, + (params.mask&KADM5_CONFIG_KADMIND_PORT)? + params.kadmind_port:0)) { + krb5_klog_syslog(LOG_ERR, "%s: %s while initializing network", + programname, error_message(code)); + fprintf(stderr, "%s: %s while initializing network\n", + programname, error_message(code)); + + exit(1); + } + + if ( +#if POSIX_SETJMP + sigsetjmp(terminal_jmp, 1) == 0 +#else /* POSIX_SETJMP */ + setjmp(terminal_jmp) == 0 +#endif /* POSIX_SETJMP */ + ) + { + if (code = net_dispatch(context, !nofork)) { + krb5_klog_syslog(LOG_ERR, "%s: %s while dispatching requests", + programname, error_message(code)); + fprintf(stderr, "%s: %s while dispatching requests\n", + programname, error_message(code)); + + exit(1); + } + } + + net_finish(context, debug_level); + + krb5_klog_syslog(LOG_INFO, "finished, exiting"); + krb5_klog_close(); + exit(2); +} + +krb5_error_code key_open_db(krb5_context context) +{ + return(kadm5_init("kadmind", NULL, + NULL, ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &global_server_handle)); +} + +krb5_error_code key_close_db(krb5_context context) +{ + kadm5_destroy(global_server_handle); + return(0); +} + +krb5_int32 +pwd_change(kcontext, debug_level, auth_context, ticket, + olddata, newdata, err_str) + krb5_context kcontext; + int debug_level; + krb5_auth_context auth_context; + krb5_ticket *ticket; + krb5_data *olddata; + krb5_data *newdata; + char err_str[]; +{ + kadm5_ret_t ret; + krb5_int32 now; + kadm5_policy_ent_rec pol; + kadm5_principal_ent_rec princ; + krb5_principal principal; + + /* Make sure the ticket is initial, otherwise don't trust it */ + if ((ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) { + return(KRB5_ADM_NOT_IN_TKT); + } + + /* a principal can always change its own password, so there's no + acl check to do here */ + + principal = ticket->enc_part2->client; + + /* check to see if the min_time has passed. this is stolen + from chpass_principal_wrapper */ + + if (ret = krb5_timeofday(kcontext, &now)) { + sprintf(err_str, error_message(ret)); + return(KRB5_ADM_SYSTEM_ERROR); + } + + if((ret = kadm5_get_principal(global_server_handle, principal, + &princ, + KADM5_PRINCIPAL_NORMAL_MASK)) != + KADM5_OK) { + sprintf(err_str, error_message(ret)); + return(KRB5_ADM_SYSTEM_ERROR); + } + if(princ.aux_attributes & KADM5_POLICY) { + if((ret=kadm5_get_policy(global_server_handle, + princ.policy, &pol)) != KADM5_OK) { + (void) kadm5_free_principal_ent(global_server_handle, &princ); + sprintf(err_str, error_message(ret)); + return(KRB5_ADM_SYSTEM_ERROR); + } + if((now - princ.last_pwd_change) < pol.pw_min_life && + !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) kadm5_free_policy_ent(global_server_handle, &pol); + (void) kadm5_free_principal_ent(global_server_handle, &princ); + sprintf(err_str, error_message(ret)); + return(KRB5_ADM_PW_UNACCEPT); + } + if (ret = kadm5_free_policy_ent(global_server_handle, &pol)) { + (void) kadm5_free_principal_ent(global_server_handle, &princ); + sprintf(err_str, error_message(ret)); + return(KRB5_ADM_SYSTEM_ERROR); + } + } + if (ret = kadm5_free_principal_ent(global_server_handle, &princ)) { + sprintf(err_str, error_message(ret)); + return(KRB5_ADM_SYSTEM_ERROR); + } + + /* ok, it's not too early to change the password. change it. */ + + if (ret = kadm5_chpass_principal_util(global_server_handle, + principal, + newdata->data, + NULL, + err_str)) + return(KRB5_ADM_PW_UNACCEPT); + + return(KRB5_ADM_SUCCESS); +} diff --git a/src/kadmin/v5passwdd/proto_serv.c b/src/kadmin/v5passwdd/proto_serv.c new file mode 100644 index 000000000..d87a71a64 --- /dev/null +++ b/src/kadmin/v5passwdd/proto_serv.c @@ -0,0 +1,860 @@ +/* + * kadmin/v5server/proto_serv.c + * + * Copyright 1995 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +/* + * proto_serv.c - Engage in protocol. This module reflects the connection + * protocol as implemented in lib/krb5/os/adm_conn.c. Any changes + * in one module must be reflected in the other. + */ +#include +#define NEED_SOCKETS +#include "k5-int.h" +#include "com_err.h" +#include "kadm5_defs.h" +#include "adm.h" +#include "adm_proto.h" +#include + +static const char *proto_addrs_msg = "\004%d: cannot get memory for addresses"; +static const char *proto_rcache_msg = "\004%d: cannot get replay cache"; +static const char *proto_ap_req_msg = "\004%d: error reading AP_REQ message"; +static const char *proto_auth_con_msg = "\004%d: cannot get authorization context"; +static const char *proto_rd_req_msg = "\004%d: cannot decode AP_REQ message"; +static const char *proto_mk_rep_msg = "\004%d: cannot generate AP_REP message"; +static const char *proto_wr_rep_msg = "\004%d: cannot write AP_REP message"; +static const char *proto_conn_abort_msg = "\007%d: connection destroyed by client"; +static const char *proto_seq_err_msg = "\004%d: protocol sequence violation"; +static const char *proto_rd_cmd_msg = "\004%d: cannot read administrative protocol command"; +static const char *proto_db_open_msg = "\004%d: cannot open database"; +static const char *proto_db_close_msg = "\004%d: cannot close database"; +static const char *proto_wr_reply_msg = "\004%d: cannot write administrative protocol reply"; +static const char *proto_fmt_reply_msg = "\004%d: cannot format administrative protocol reply"; +extern char *programname; + +static int proto_proto_timeout = -1; +static int proto_debug_level = 0; +#if POSIX_SETJMP +static sigjmp_buf timeout_jmp; +#else /* POSIX_SETJMP */ +static jmp_buf timeout_jmp; +#endif /* POSIX_SETJMP */ + +static krb5_sigtype +proto_alarmclock(signo) + int signo; +{ +#if POSIX_SETJMP + siglongjmp(timeout_jmp, 1); +#else /* POSIX_SETJMP */ + longjmp(timeout_jmp, 1); +#endif /* POSIX_SETJMP */ + /* NOTREACHED */ +} + +krb5_error_code +proto_init(kcontext, debug_level, timeo) + krb5_context kcontext; + int debug_level; + int timeo; +{ + krb5_error_code kret; + + proto_debug_level = debug_level; + DPRINT(DEBUG_CALLS, proto_debug_level, + ("* proto_init(timeo=%d)\n", timeo)); + kret = 0; + proto_proto_timeout = timeo; + DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_init() = %d\n", kret)); + return(kret); +} + +void +proto_finish(kcontext, debug_level) + krb5_context kcontext; + int debug_level; +{ + DPRINT(DEBUG_CALLS, proto_debug_level, ("* proto_finish()\n")); + DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_finish()\n")); +} + +krb5_error_code +proto_serv(kcontext, my_id, cl_sock, sv_p, cl_p) + krb5_context kcontext; + krb5_int32 my_id; + int cl_sock; + void *sv_p; + void *cl_p; +{ + krb5_error_code kret; + struct sockaddr_in *cl_addr; + struct sockaddr_in *sv_addr; + + krb5_data in_data; + krb5_data out_data; + krb5_rcache rcache; + krb5_auth_context auth_context; + krb5_flags ap_options; + krb5_ticket *ticket; + krb5_address *local; + krb5_address *remote; + +#if POSIX_SIGNALS + struct sigaction s_action; +#endif /* POSIX_SIGNALS */ + + char *curr_lang = (char *) NULL; + krb5_boolean mime_setting = 0; + + krb5_int32 num_args; + krb5_data *arglist; + + krb5_boolean db_opened; + + cl_addr = (struct sockaddr_in *) cl_p; + sv_addr = (struct sockaddr_in *) sv_p; + DPRINT(DEBUG_CALLS, proto_debug_level, + ("* proto_serv(id=%d, sock=%d, local=%x, remote=%x)\n", + my_id, cl_sock, + ntohl(sv_addr->sin_addr.s_addr), + ntohl(cl_addr->sin_addr.s_addr))); + + /* Initialize */ + memset((char *) &in_data, 0, sizeof(in_data)); + memset((char *) &out_data, 0, sizeof(out_data)); + num_args = 0; + local = (krb5_address *) NULL; + remote = (krb5_address *) NULL; + ticket = (krb5_ticket *) NULL; + rcache = (krb5_rcache) NULL; +#if POSIX_SIGNALS + (void) sigemptyset(&s_action.sa_mask); + s_action.sa_flags = 0; +#endif /* POSIX_SIGNALS */ + db_opened = 0; + + /* Get memory for addresses */ + local = (krb5_address *) malloc(sizeof(krb5_address)); + remote = (krb5_address *) malloc(sizeof(krb5_address)); + if (!local || !remote) { + kret = ENOMEM; + com_err(programname, kret, proto_addrs_msg, my_id); + goto cleanup; + } + + local->contents = (krb5_octet *) malloc(sizeof(struct in_addr)); + remote->contents = (krb5_octet *) malloc(sizeof(struct in_addr)); + if (!local->contents || !remote->contents) { + kret = ENOMEM; + com_err(programname, kret, proto_addrs_msg, my_id); + goto cleanup; + } + + /* + * First setup the replay cache. + */ + if (kret = krb5_get_server_rcache(kcontext, + krb5_princ_component(kcontext, + net_server_princ(), + 0), + &rcache)) { + com_err(programname, kret, proto_rcache_msg, my_id); + goto cleanup; + } + + /* Initialize the auth context */ + if (kret = krb5_auth_con_init(kcontext, &auth_context)) { + com_err(programname, kret, proto_auth_con_msg, my_id); + goto cleanup; + } + + krb5_auth_con_setrcache(kcontext, auth_context, rcache); + + /* + * Set up addresses. + */ + local->addrtype = remote->addrtype = ADDRTYPE_INET; + local->length = remote->length = sizeof(struct in_addr); + memcpy((char *) local->contents, + (char *) &sv_addr->sin_addr, + sizeof(struct in_addr)); + memcpy((char *) remote->contents, + (char *) &cl_addr->sin_addr, + sizeof(struct in_addr)); + krb5_auth_con_setflags(kcontext, auth_context, + KRB5_AUTH_CONTEXT_RET_SEQUENCE| + KRB5_AUTH_CONTEXT_DO_SEQUENCE); + krb5_auth_con_setaddrs(kcontext, auth_context, local, remote); + + DPRINT(DEBUG_PROTO, proto_debug_level, + ("= %d:read message(local=%x, remote=%x)\n", + my_id, + ntohl(sv_addr->sin_addr.s_addr), + ntohl(cl_addr->sin_addr.s_addr))); + /* Now, read in the AP_REQ message and decode it. */ + if (kret = krb5_read_message(kcontext, + (krb5_pointer) &cl_sock, + &in_data)) { + com_err(programname, kret, proto_ap_req_msg, my_id); + goto cleanup; + } + + DPRINT(DEBUG_PROTO, proto_debug_level, + ("= %d:parse message(%d bytes)\n", my_id, in_data.length)); + + /* Parse the AP_REQ message */ + if (kret = krb5_rd_req(kcontext, + &auth_context, + &in_data, + net_server_princ(), + key_keytab_id(), + &ap_options, + &ticket)) { + com_err(programname, kret, proto_rd_req_msg, my_id); + goto err_reply; + } + + DPRINT(DEBUG_PROTO, proto_debug_level, + ("= %d:check AP_REQ(options are %x)\n", my_id, ap_options)); + /* Check our options */ + if ((ap_options & AP_OPTS_MUTUAL_REQUIRED) == 0) { + kret = KRB5KRB_AP_ERR_MSG_TYPE; + goto err_reply; + } + + DPRINT(DEBUG_PROTO, proto_debug_level, + ("= %d:make AP_REP\n", my_id)); + if (kret = krb5_mk_rep(kcontext, auth_context, &out_data)) { + com_err(programname, kret, proto_mk_rep_msg, my_id); + goto cleanup; + } + + DPRINT(DEBUG_PROTO, proto_debug_level, + ("= %d:write AP_REP(%d bytes)\n", my_id, out_data.length)); + if (kret = krb5_write_message(kcontext, + (krb5_pointer) &cl_sock, + &out_data)) { + com_err(programname, kret, proto_wr_rep_msg, my_id); + goto cleanup; + } + + /* + * Initialization is now complete. + * + * If enabled, the protocol times out after proto_proto_timeout seconds. + */ + if ( +#if POSIX_SETJMP + sigsetjmp(timeout_jmp, 1) == 0 +#else /* POSIX_SETJMP */ + setjmp(timeout_jmp) == 0 +#endif /* POSIX_SETJMP */ + ) { + if (proto_proto_timeout > 0) { +#if POSIX_SIGNALS + s_action.sa_handler = proto_alarmclock; + (void) sigaction(SIGALRM, &s_action, (struct sigaction *) NULL); +#else /* POSIX_SIGNALS */ + signal(SIGALRM, proto_alarmclock); +#endif /* POSIX_SIGNALS */ + } + /* + * Loop forever - or until somebody puts us out of our misery. + */ + while (1) { + krb5_int32 cmd_error; + char err_str[1024]; + krb5_int32 cmd_repl_ncomps; + krb5_data *cmd_repl_complist; + int do_quit; + + /* + * Read a command and figure out what to do. + */ + if (proto_proto_timeout > 0) + alarm(proto_proto_timeout); + num_args = 0; + DPRINT(DEBUG_PROTO, proto_debug_level, + ("= %d:waiting for command\n", my_id)); + kret = krb5_read_adm_cmd(kcontext, + (krb5_pointer) &cl_sock, + auth_context, + &num_args, + &arglist); + if (proto_proto_timeout > 0) + alarm(0); + if (kret) { + /* + * It's OK to have connections abort here. + */ + if (kret == ECONNABORTED) { + com_err(programname, kret, proto_conn_abort_msg, my_id); + kret = 0; + } + else if (kret == KRB5KRB_AP_ERR_BADORDER) { + com_err(programname, kret, proto_seq_err_msg, my_id); + kret = 0; + } + else + com_err(programname, kret, proto_rd_cmd_msg, my_id); + goto cleanup; + } + + cmd_error = KRB5_ADM_SUCCESS; + do_quit = 0; + + /* + * First open the database. We only have it open for the + * lifetime of a command so that we are sure to close it after + * performing an update. This also reduces the likelihood + * that somebody'll have stale data lying around since we're + * most likely going to change something here. + */ + if ((kret = key_open_db(kcontext))) { + com_err(programname, kret, proto_db_open_msg, my_id); + goto cleanup; + } + else + db_opened = 1; + + /* + * Now check our arguments. + */ + DPRINT(DEBUG_PROTO, proto_debug_level, + ("= %d:parse command\n", my_id)); + cmd_repl_ncomps = 0; + cmd_repl_complist = (krb5_data *) NULL; + if (num_args > 0) { + if (!strcasecmp(arglist[0].data, KRB5_ADM_QUIT_CMD)) { + /* + * QUIT command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:QUIT command\n", my_id)); + /* QUIT takes no arguments */ + if (num_args == 1) { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:QUIT command syntax OK\n", my_id)); + do_quit = 1; + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:QUIT command syntax BAD\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + strcpy(err_str, + "Bad argument list format for quit command."); + } + } + else if (!strcasecmp(arglist[0].data, KRB5_ADM_CHECKPW_CMD)) { + /* + * CHECKPW command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHECKPW command\n", my_id)); + if (num_args == 2) { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHECKPW command syntax OK\n", my_id)); + cmd_error = 0; + err_str[0] = '\0'; +#if 0 + cmd_error = pwd_check(kcontext, + proto_debug_level, + auth_context, + ticket, + &arglist[1], + &err_str); +#endif + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHECKPW command syntax BAD\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + + strcpy(err_str, + "Bad argument list format for checkpw command."); + } + } + else if (!strcasecmp(arglist[0].data, KRB5_ADM_CHANGEPW_CMD)) { + /* + * CHANGEPW command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHANGEPW command\n", my_id)); + if (num_args == 3) { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHANGEPW command syntax OK\n", my_id)); + cmd_error = pwd_change(kcontext, + proto_debug_level, + auth_context, + ticket, + &arglist[1], + &arglist[2], + &err_str); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHANGEPW command syntax BAD\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + strcpy(err_str, + "Bad argument list format for changepw command."); + } + } +#if 0 +#ifdef MOTD_SUPPORTED + else if (!strcasecmp(arglist[0].data, KRB5_ADM_MOTD_CMD)) { + /* + * MOTD command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:MOTD command\n", my_id)); + if (num_args <= 2) { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:MOTD command syntax OK\n", my_id)); + printf("@@@ motd command "); + if (num_args == 2) + printf("context is %s", arglist[2].data); + printf("\n"); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:MOTD command syntax BAD\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } +#endif /* MOTD_SUPPORTED */ +#ifdef MIME_SUPPORTED + else if (!strcasecmp(arglist[0].data, KRB5_ADM_MIME_CMD)) { + /* + * MIME command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:MIME command\n", my_id)); + if (num_args == 1) { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:MIME command syntax OK\n", my_id)); + mime_setting = 1; + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:MIME command syntax BAD\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } +#endif /* MIME_SUPPORTED */ +#ifdef LANGUAGES_SUPPORTED + else if (!strcasecmp(arglist[0].data, KRB5_ADM_LANGUAGE_CMD)) { + /* + * LANGUAGE command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:LANGUAGE command\n", my_id)); + if (num_args == 2) { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:LANGUAGE command syntax OK\n", my_id)); + if (output_lang_supported(arglist[1].data)) { + if (curr_lang) + free(curr_lang); + curr_lang = (char *) + malloc(strlen(arglist[1].data)); + if (curr_lang) + strcpy(curr_lang, arglist[1].data); + } + else + cmd_error = KRB5_ADM_LANG_NOT_SUPPORTED; + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:LANGUAGE command syntax BAD\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } +#endif /* LANGUAGES_SUPPORTED */ + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_ADD_PRINC_CMD)) { + /* + * ADD PRINCIPAL command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:ADD PRINCIPAL command\n", my_id)); + /* At least one argument */ + if (num_args > 1) { + cmd_error = admin_add_principal(kcontext, + proto_debug_level, + ticket, + num_args-1, + &arglist[1]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:ADD PRINCIPAL command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_DEL_PRINC_CMD)) { + /* + * DELETE PRINCIPAL command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:DELETE PRINCIPAL command\n", my_id)); + /* Only one argument */ + if (num_args == 2) { + cmd_error = admin_delete_principal(kcontext, + proto_debug_level, + ticket, + &arglist[1]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:DELETE PRINCIPAL command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_REN_PRINC_CMD)) { + /* + * RENAME PRINCIPAL command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:RENAME PRINCIPAL command\n", my_id)); + /* Two arguments */ + if (num_args == 3) { + cmd_error = admin_rename_principal(kcontext, + proto_debug_level, + ticket, + &arglist[1], + &arglist[2]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:RENAME PRINCIPAL command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_MOD_PRINC_CMD)) { + /* + * MODIFY PRINCIPAL command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:MODIFY PRINCIPAL command\n", my_id)); + /* At least one argument */ + if (num_args > 1) { + cmd_error = admin_modify_principal(kcontext, + proto_debug_level, + ticket, + num_args-1, + &arglist[1]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:MODIFY PRINCIPAL command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_CHG_OPW_CMD)) { + /* + * CHANGE OTHER'S PASSWORD command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHANGE OTHER'S PASSWORD command\n", my_id)); + /* Two arguments */ + if (num_args == 3) { + cmd_error = admin_change_opwd(kcontext, + proto_debug_level, + ticket, + &arglist[1], + &arglist[2]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHANGE OTHER'S PASSWORD command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_CHG_ORPW_CMD)) { + /* + * CHANGE OTHER'S RANDOM PASSWORD command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHANGE OTHER'S RANDOM PASSWORD command\n", my_id)); + /* One argument */ + if (num_args == 2) { + cmd_error = admin_change_orandpwd(kcontext, + proto_debug_level, + ticket, + &arglist[1]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHANGE OTHER'S RANDOM PASSWORD command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_INQ_PRINC_CMD)) { + /* + * INQUIRE PRINCIPAL command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:INQUIRE PRINCIPAL command\n", my_id)); + /* One argument */ + if (num_args == 2) { + cmd_error = admin_inquire(kcontext, + proto_debug_level, + ticket, + &arglist[1], + &cmd_repl_ncomps, + &cmd_repl_complist); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:INQUIRE PRINCIPAL command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_EXT_KEY_CMD)) { + /* + * EXTRACT KEY command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:EXTRACT KEY command\n", my_id)); + /* Two arguments */ + if (num_args == 3) { + cmd_error = admin_extract_key(kcontext, + proto_debug_level, + ticket, + &arglist[1], + &arglist[2], + &cmd_repl_ncomps, + &cmd_repl_complist); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:EXTRACT KEY command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_ADD_KEY_CMD)) { + /* + * ADD KEY command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:ADD KEY command\n", my_id)); + /* Must have at least three arguments */ + if (num_args > 3) { + cmd_error = admin_add_key(kcontext, + proto_debug_level, + ticket, + num_args-1, + &arglist[1]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:ADD KEY command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } + else if (!strcasecmp(arglist[0].data, + KRB5_ADM_DEL_KEY_CMD)) { + /* + * DELETE KEY command handling here. + */ + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:DELETE KEY command\n", my_id)); + /* At least three arguments */ + if (num_args > 3) { + cmd_error = admin_delete_key(kcontext, + proto_debug_level, + ticket, + num_args-1, + &arglist[1]); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:DELETE KEY command syntax BAD\n", + my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KADM_BAD_ARGS; + } + } +#endif /* 0 */ + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:UNKNOWN command %s\n", my_id, + arglist[0].data)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + sprintf(err_str, "Command %s not supported.", arglist[0]); + } + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:NO command!\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + strcpy(err_str, "No command in message."); + } + + /* + * Close the database. + */ + if ((kret = key_close_db(kcontext))) { + com_err(programname, kret, proto_db_close_msg, my_id); + goto cleanup; + } + else + db_opened = 0; + + /* + * Now make the reply. + */ + DPRINT(DEBUG_PROTO, proto_debug_level, + ("= %d:sending reply(stat=%d)\n", my_id, cmd_error)); + if (cmd_error == KRB5_ADM_SUCCESS) { + kret = krb5_send_adm_reply(kcontext, + (krb5_pointer) &cl_sock, + auth_context, + cmd_error, + cmd_repl_ncomps, + cmd_repl_complist); + if (kret) { + com_err(programname, kret, proto_wr_reply_msg, my_id); + goto cleanup; + } + } + else { + char *adm_errmsg; + krb5_data reply_comps; + + reply_comps.data = err_str; + reply_comps.length = strlen(err_str); + kret = krb5_send_adm_reply(kcontext, + (krb5_pointer) &cl_sock, + auth_context, + cmd_error, + 1, + &reply_comps); + if (kret) { + com_err(programname, kret, proto_wr_reply_msg, my_id); + goto cleanup; + } + } + if (cmd_repl_ncomps > 0) + krb5_free_adm_data(kcontext, + cmd_repl_ncomps, + cmd_repl_complist); + + if (do_quit) + break; + krb5_free_adm_data(kcontext, num_args, arglist); + } + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, ("connection timed out")); + } + + + err_reply: + if (kret) { + krb5_error_code er_kret; + krb5_error errbuf; + char *errmsg; + krb5_data errout; + + memset((char *) &errbuf, 0, sizeof(errbuf)); + krb5_us_timeofday(kcontext, &errbuf.stime, &errbuf.susec); + errbuf.server = net_server_princ(); + errbuf.error = kret - ERROR_TABLE_BASE_krb5; + if (errbuf.error > 127) + errbuf.error = KRB5KRB_ERR_GENERIC; + /* Format the error message in our language */ + errmsg = error_message(kret); + errbuf.text.length = strlen(errmsg); + errbuf.text.data = errmsg; + er_kret = krb5_mk_error(kcontext, &errbuf, &errout); + if (!er_kret) + krb5_write_message(kcontext, (krb5_pointer) &cl_sock, &errout); + free(errbuf.text.data); + krb5_xfree(errout.data); + } + + cleanup: + /* If the alarm was set, make sure it's cancelled */ + if (proto_proto_timeout > 0) + alarm(0); + if (ticket) + krb5_free_ticket(kcontext, ticket); + /* + * Don't need to close the replay cache because it's attached to the + * auth context. + */ + if (auth_context) + krb5_auth_con_free(kcontext, auth_context); + if (curr_lang) + free(curr_lang); + if (num_args) + krb5_free_adm_data(kcontext, num_args, arglist); + if (in_data.data) + krb5_xfree(in_data.data); + if (out_data.data) + krb5_xfree(out_data.data); + if (local && local->contents) + free(local->contents); + if (remote && remote->contents) + free(remote->contents); + if (local) + free(local); + if (remote) + free(remote); + if (db_opened) + key_close_db(kcontext); + close(cl_sock); + + done: + DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_serv() = %d\n", kret)); + return(kret); +} diff --git a/src/kadmin/v5passwdd/srv_net.c b/src/kadmin/v5passwdd/srv_net.c new file mode 100644 index 000000000..eefc8981f --- /dev/null +++ b/src/kadmin/v5passwdd/srv_net.c @@ -0,0 +1,856 @@ +/* + * kadmin/v5server/srv_net.c + * + * Copyright 1995 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +/* + * srv_net.c - handle networking functions of the administrative server. + */ +#include +#include +#include +#include +#ifdef USE_PTHREADS +#include +#endif /* USE_PTHREADS */ + +#define NEED_SOCKETS +#include "k5-int.h" +#include "com_err.h" +#include "kadm5_defs.h" +#include "adm.h" + +#if HAVE_SYS_SELECT_H +#include +#endif + +/* linux doesn't have SOMAXCONN */ +#ifndef SOMAXCONN +#define SOMAXCONN 5 +#endif + +/* + * This module can use the pthreads library. To do so, define USE_PTHREADS. + * You'll need to find out what else pthreads requires (e.g. -lmach -lc_r + * under OSF/1). + */ +#ifdef USE_PTHREADS +#define net_slave_type pthread_t * +#ifndef MAX_SLAVES +#define MAX_SLAVES 2*SOMAXCONN +#endif /* MAX_SLAVES */ +#else /* USE_PTHREADS */ +#define net_slave_type pid_t +#ifndef MAX_SLAVES +#define MAX_SLAVES 2*SOMAXCONN +#endif /* MAX_SLAVES */ +#endif /* USE_PTHREADS */ +#define NET_SLAVE_FULL_SLEEP 2 /* seconds */ + +/* + * Slave information storage. + */ +typedef struct _net_slave_info { + int sl_inuse; + net_slave_type sl_id; + krb5_context sl_context; + int sl_socket; + struct sockaddr_in sl_local_addr; /* local address */ + struct sockaddr_in sl_remote_addr; /* remote address */ +} net_slave_info; + +/* + * Error messages. + */ +static const char *net_waiterr_msg = "\004child wait failed - cannot reap children"; +static const char *net_def_realm_fmt = "%s: cannot get default realm (%s).\n"; +static const char *net_no_mem_fmt = "%s: cannot get memory.\n"; +static const char *net_parse_srv_fmt = "%s: cannot parse server name %s (%s).\n"; +static const char *net_no_hostname_fmt = "%s: cannot get our host name (%s).\n"; +static const char *net_no_hostent_fmt = "%s: cannot get our host entry (%s).\n"; +static const char *net_no_servent_fmt = "%s: cannot get service entry for %s (%s).\n"; +static const char *net_sockerr_fmt = "%s: cannot open network socket (%s).\n"; +static const char *net_soerr_fmt = "%s: cannot set socket options (%s).\n"; +static const char *net_binderr_fmt = "%s: cannot bind to network address (%s).\n"; + +static const char *net_select_fmt = "\004select failed"; +static const char *net_cl_disp_fmt = "\004client dispatch failed"; +static const char *net_not_ready_fmt = "\004select error - no socket to read"; +static const char *net_dispatch_msg = "network dispatch"; + +static int net_debug_level = 0; + +static char *net_service_name = (char *) NULL; +static int net_service_princ_init = 0; +static krb5_principal net_service_principal = (krb5_principal) NULL; +static int net_server_addr_init = 0; +static struct sockaddr_in net_server_addr; +static int net_listen_socket = -1; +static int net_slaves_active = 0; +static int net_max_slaves = 0; +static net_slave_info *net_slave_table = (net_slave_info *) NULL; + +#if POSIX_SETJMP +static sigjmp_buf shutdown_jmp; +#else /* POSIX_SETJMP */ +static jmp_buf shutdown_jmp; +#endif /* POSIX_SETJMP */ + +extern char *programname; + +/* + * net_find_free_entry() - Find a free entry in the slave table. + */ +static net_slave_info * +net_find_free_entry() +{ + int i, found; + + /* Find a table entry */ + while (1) { + found = 0; + for (i=0; isl_inuse = 0; +} + +/* + * net_shutdown() - Destroy all slaves on signal reception + */ +static krb5_sigtype +net_shutdown(signo) + int signo; +{ + int i; + + /* Loop through all slaves */ + for (i=0; i 0) { + DPRINT(DEBUG_SPROC, net_debug_level, + ("| process %d finished with %d\n", deadmeat, child_exit)); + slent = net_find_slave(deadmeat); + if (slent) { + net_free_slave_entry(slent); + } + else { + DPRINT(DEBUG_SPROC, net_debug_level, + ("| cannot find slave entry for %d\n", deadmeat)); + } + } + if ((deadmeat == -1) && (errno != ECHILD)) + com_err(programname, errno, net_waiterr_msg); +} +#endif /* USE_PTHREADS */ + +#if USE_PTHREADS +/* + * net_slave_proto() - pthread main routine. + */ +static krb5_error_code +net_slave_proto(stent) + net_slave_info *stent; +{ + krb5_error_code kret; + + DPRINT(DEBUG_CALLS, net_debug_level, + ("* net_slave_proto()\n")); + DPRINT(DEBUG_SPROC, net_debug_level, + ("| thread %d starting\n", stent->sl_id)); + kret = proto_serv(stent->sl_context, + (krb5_int32) stent->sl_id, + stent->sl_socket, + &stent->sl_local_addr, + &stent->sl_remote_addr); + DPRINT(DEBUG_SPROC, net_debug_level, + ("| thread %d finished with %d\n", stent->sl_id, kret)); + DPRINT(DEBUG_CALLS, net_debug_level, + ("* net_slave_proto() = %d\n", kret)); + net_free_slave_entry(stent); + return(kret); +} +#endif /* USE_PTHREADS */ + +/* + * net_dispatch_client() - Handle client dispatch. + */ +static krb5_error_code +net_dispatch_client(kcontext, listen_sock, conn_sock, client_addr) + krb5_context kcontext; + int listen_sock; + int conn_sock; + struct sockaddr_in *client_addr; +{ + krb5_error_code kret; + net_slave_info *slent; + + DPRINT(DEBUG_CALLS, net_debug_level, + ("* net_dispatch_client(listen=%d)\n", listen_sock)); + kret = 0; + + /* Find a free entry */ + slent = net_find_free_entry(); + + /* Initialize the slave entry */ + slent->sl_context = kcontext; + slent->sl_socket = conn_sock; + memcpy((char *) &slent->sl_remote_addr, + (char *) client_addr, + sizeof(struct sockaddr_in)); + memcpy((char *) &slent->sl_local_addr, + (char *) &net_server_addr, + sizeof(struct sockaddr_in)); + +#ifdef DEBUG + if ((net_debug_level & DEBUG_NOSLAVES) == 0) { +#endif /* DEBUG */ + /* Do a real slave creation */ +#if USE_PTHREADS + if (!slent->sl_id) + slent->sl_id = (pthread_t *) malloc(sizeof(pthread_t)); + if (slent->sl_id == (pthread_t *) NULL) { + kret = ENOMEM; + goto done; + } + if (kret = pthread_create(slent->sl_id, + pthread_attr_default, + (pthread_startroutine_t) net_slave_proto, + (pthread_addr_t) slent)) { + kret = errno; + goto done; + } + if (pthread_detach(slent->sl_id)) { + DPRINT(DEBUG_SPROC, net_debug_level, + ("| (%d) child thread %d detach failed (%d)\n", + getpid(), slent->sl_id, errno)); + } + DPRINT(DEBUG_SPROC, net_debug_level, + ("| (%d) created child thread %d\n", + getpid(), slent->sl_id)); +#else /* USE_PTHREADS */ + slent->sl_id = fork(); + if (slent->sl_id < 0) { + kret = errno; + slent->sl_inuse = 0; + goto done; + } + + if (slent->sl_id > 0) { + /* parent */ + DPRINT(DEBUG_SPROC, net_debug_level, + ("| (%d) created child process %d\n", + getpid(), slent->sl_id)); + close(conn_sock); + kret = 0; + goto done; + } + else { +#if POSIX_SIGNALS + struct sigaction s_action; +#endif /* POSIX_SIGNALS */ + + /* child */ +#if POSIX_SIGNALS + (void) sigemptyset(&s_action.sa_mask); + s_action.sa_flags = 0; + /* Ignore SIGINT, SIGTERM, SIGHUP, SIGQUIT and SIGPIPE */ + s_action.sa_handler = SIG_IGN; + (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL); + (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL); + /* Restore to default SIGCHLD */ + s_action.sa_handler = SIG_DFL; + (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL); +#else /* POSIX_SIGNALS */ + signal(SIGINT, SIG_IGN); /* Ignore SIGINT */ + signal(SIGTERM, SIG_IGN); /* Ignore SIGTERM */ + signal(SIGHUP, SIG_IGN); /* Ignore SIGHUP */ + signal(SIGQUIT, SIG_IGN); /* Ignore SIGQUIT */ + signal(SIGPIPE, SIG_IGN); /* Ignore SIGPIPE */ + signal(SIGCHLD, SIG_DFL); /* restore SIGCHLD handling */ +#endif /* POSIX_SIGNALS */ + close(listen_sock); + slent->sl_id = getpid(); + DPRINT(DEBUG_SPROC, net_debug_level, + ("| process %d starting\n", slent->sl_id)); + kret = proto_serv(slent->sl_context, + (krb5_int32) slent->sl_id, + slent->sl_socket, + &slent->sl_local_addr, + &slent->sl_remote_addr); + DPRINT(DEBUG_SPROC, net_debug_level, + ("| process %d exiting with %d\n", getpid(), kret)); + exit(kret); + } +#endif /* USE_PTHREADS */ +#ifdef DEBUG + } + else { + net_slave_info *sl1; +#if POSIX_SIGNALS + struct sigaction s_action; +#endif /* POSIX_SIGNALS */ + + /* + * Ignore SIGPIPE. + */ +#if POSIX_SIGNALS + (void) sigemptyset(&s_action.sa_mask); + s_action.sa_flags = 0; + s_action.sa_handler = SIG_IGN; + (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL); +#else /* POSIX_SIGNALS */ + signal(SIGPIPE, SIG_IGN); /* Ignore SIGPIPE */ +#endif /* POSIX_SIGNALS */ + DPRINT(DEBUG_SPROC, net_debug_level, + ("| (%d) not doing child creation\n", getpid())); + slent->sl_id = (net_slave_type) getpid(); + kret = proto_serv(slent->sl_context, + (krb5_int32) slent->sl_id, + slent->sl_socket, + &slent->sl_local_addr, + &slent->sl_remote_addr); + sl1 = net_find_slave(slent->sl_id); + if (sl1) + net_free_slave_entry(sl1); + DPRINT(DEBUG_SPROC, net_debug_level, + ("| (%d) returned with %d\n", getpid(), kret)); + kret = 0; + } +#endif /* DEBUG */ + done: + DPRINT(DEBUG_CALLS, net_debug_level, + ("X net_dispatch_client() = %d\n", kret)); + return(kret); +} + +/* + * net_init() - Initialize network context. + */ +krb5_error_code +net_init(kcontext, realm, debug_level, port) + krb5_context kcontext; + char * realm; + int debug_level; + krb5_int32 port; +{ + krb5_error_code kret; + char our_host_name[MAXHOSTNAMELEN]; + struct hostent *our_hostent; + struct servent *our_servent; + + net_debug_level = debug_level; + DPRINT(DEBUG_CALLS, net_debug_level, ("* net_init(port=%d)\n", port)); + + /* Allocate the slave table */ + net_slave_table = (net_slave_info *) + malloc((size_t) (MAX_SLAVES * sizeof(net_slave_info))); + /* Make our service name */ + net_service_name = (char *) malloc(strlen(realm) + + strlen(KRB5_ADM_SERVICE_INSTANCE) + 2); + if ((net_service_name == (char *) NULL) || + (net_slave_table == (net_slave_info *) NULL)) { + kret = ENOMEM; + fprintf(stderr, net_no_mem_fmt, programname); + goto done; + } + (void) sprintf(net_service_name, "%s%s%s", + KRB5_ADM_SERVICE_INSTANCE, "/", realm); + memset((char *) net_slave_table, 0, + (size_t) (MAX_SLAVES * sizeof(net_slave_info))); + net_max_slaves = MAX_SLAVES; + DPRINT(DEBUG_HOST, net_debug_level, + ("- name of service is %s\n", net_service_name)); + + /* Now formulate the principal name */ + if (kret = krb5_parse_name(kcontext, + net_service_name, + &net_service_principal)) { + fprintf(stderr, net_parse_srv_fmt, programname, net_service_name, + error_message(kret)); + goto done; + } + net_service_princ_init = 1; + +#ifdef KRB5_USE_INET + /* Now get our host name/entry */ + if (gethostname(our_host_name, sizeof(our_host_name))) { + kret = errno; + fprintf(stderr, net_no_hostname_fmt, programname, error_message(kret)); + goto done; + } + if (!(our_hostent = gethostbyname(our_host_name))) { + kret = KRB5_ERR_BAD_HOSTNAME; /* perhaps put h_errno in the msg */ + fprintf(stderr, net_no_hostent_fmt, programname, error_message(kret)); + goto done; + } + DPRINT(DEBUG_HOST, net_debug_level, + ("- name of host is %s\n", our_hostent->h_name)); + + /* Now initialize our network address */ + net_server_addr.sin_family = AF_INET; + memcpy((char *) &net_server_addr.sin_addr, + (char *) our_hostent->h_addr, + sizeof(net_server_addr.sin_addr)); + DPRINT(DEBUG_HOST, net_debug_level, + ("- address of host is %x\n", + ntohl(net_server_addr.sin_addr.s_addr))); + + /* + * Fill in the port address. + * If the port is supplied by the invoker, then use that one. + * If not, then try the profile, and if all fails, then use the service + * entry. + */ + if (port > 0) { + net_server_addr.sin_port = htons(port); + DPRINT(DEBUG_HOST, net_debug_level, + ("- service name (%s) is on port %d from options\n", + KRB5_ADM_SERVICE_NAME, + ntohs(net_server_addr.sin_port))); + } + else { + char **admin_hostlist; + const char *realm_admin_names[4]; /* XXX */ + krb5_boolean found; + + /* + * Try to get the service entry out of the profile. + */ + admin_hostlist = (char **) NULL; + realm_admin_names[0] = "realms"; + realm_admin_names[1] = realm; + realm_admin_names[2] = "admin_server"; + realm_admin_names[3] = (char *) NULL; + found = 0; +#ifndef OLD_CONFIG_FILES + if (!(kret = profile_get_values(kcontext->profile, + realm_admin_names, + &admin_hostlist))) { + int hi; + char *cport; + char *cp; + krb5_int32 pport; + int ai; + + cport = (char *) NULL; + pport = KRB5_ADM_DEFAULT_PORT; + for (hi=0; admin_hostlist[hi]; hi++) { + /* + * This knows a little too much about the format of profile + * entries. Shouldn't it just be some sort of tuple? + * + * The form is assumed to be: + * admin_server = [:[]] + */ + cp = strchr(admin_hostlist[hi], ' '); + if (cp) + *cp = '\0'; + cp = strchr(admin_hostlist[hi], '\t'); + if (cp) + *cp = '\0'; + cport = strchr(admin_hostlist[hi], ':'); + if (cport) { + *cport = '\0'; + cport++; + if (sscanf(cport, "%d", &pport) != 1) { + DPRINT(DEBUG_HOST, net_debug_level, + ("- profile entry for %s has bad port %s\n", + admin_hostlist[hi], + cport)); + pport = KRB5_ADM_DEFAULT_PORT; + } + } + /* + * We've stripped away the crud. Now check to see if the + * profile entry matches our hostname. If so, then this + * is the one to use. Additionally, check the host alias + * list. + */ + if (!strcmp(admin_hostlist[hi], our_hostent->h_name)) { + net_server_addr.sin_port = ntohs((u_short) pport); + DPRINT(DEBUG_HOST, net_debug_level, + ("- service name (%s) is on port %d from profile\n", + KRB5_ADM_SERVICE_NAME, + pport)); + found = 1; + } + else { + for (ai=0; our_hostent->h_aliases[ai]; ai++) { + if (!strcmp(admin_hostlist[hi], + our_hostent->h_aliases[ai])) { + net_server_addr.sin_port = ntohs(pport); + DPRINT(DEBUG_HOST, net_debug_level, + ("- service name (%s) is on port %d from profile and alias\n", + KRB5_ADM_SERVICE_NAME, + pport)); + found = 1; + break; + } + } + } + } + krb5_xfree(admin_hostlist); + } +#endif /* OLD_CONFIG_FILES */ + + /* + * If we didn't find an entry in the profile, then as a last gasp + * effort, attempt to find it in /etc/services. + */ + if (!found) { + /* Get the service entry out of /etc/services */ + if (!(our_servent = getservbyname(KRB5_ADM_SERVICE_NAME, "tcp"))) { + kret = errno; + fprintf(stderr, net_no_servent_fmt, programname, + KRB5_ADM_SERVICE_NAME, error_message(kret)); + goto done; + } + net_server_addr.sin_port = our_servent->s_port; + DPRINT(DEBUG_HOST, net_debug_level, + ("- service name (%s) is on port %d from services\n", + our_servent->s_name, + ntohs(our_servent->s_port))); + } + } + net_server_addr_init = 1; + + /* Now open the listen socket */ + net_listen_socket = socket(AF_INET, SOCK_STREAM, 0); + if (net_listen_socket < 0) { + kret = errno; + fprintf(stderr, net_sockerr_fmt, programname, error_message(kret)); + goto done; + } + + /* If we have a non-default port number, then allow reuse of address */ + if (net_server_addr.sin_port != htons(KRB5_ADM_DEFAULT_PORT)) { + int allowed; + + allowed = 1; + if (setsockopt(net_listen_socket, + SOL_SOCKET, + SO_REUSEADDR, + (char *) &allowed, + sizeof(allowed)) < 0) { + kret = errno; + fprintf(stderr, net_soerr_fmt, programname, error_message(kret)); + goto done; + } + } + + /* Bind socket */ + if (bind(net_listen_socket, + (struct sockaddr *) &net_server_addr, + sizeof(net_server_addr)) < 0) { + kret = errno; + fprintf(stderr, net_binderr_fmt, programname, error_message(kret)); + goto done; + } + else { + DPRINT(DEBUG_HOST, net_debug_level, + ("- bound socket %d on port\n", net_listen_socket)); + kret = 0; + } +#else /* KRB5_USE_INET */ + /* Don't know how to do anything else. */ + kret = ENOENT; +#endif /* KRB5_USE_INET */ + + done: + DPRINT(DEBUG_CALLS, net_debug_level, ("X net_init() = %d\n", kret)); + return(kret); +} + +/* + * net_finish() - Finish network context. + */ +void +net_finish(kcontext, debug_level) + krb5_context kcontext; + int debug_level; +{ + DPRINT(DEBUG_CALLS, net_debug_level, ("* net_finish()\n")); + if (net_max_slaves) { + net_max_slaves = 0; + free(net_slave_table); + } + if (net_listen_socket >= 0) + close(net_listen_socket); + if (net_service_princ_init) + krb5_free_principal(kcontext, net_service_principal); + if (net_service_name) + free(net_service_name); + DPRINT(DEBUG_CALLS, net_debug_level, ("X net_finish()\n")); +} + +/* + * net_dispatch() - Listen and dispatch request. + * + * Loop forever selecting on the listen socket. When an incoming connection + * comes in, dispatch to net_client_connect(). + */ +krb5_error_code +net_dispatch(kcontext, detached) + krb5_context kcontext; + int detached; +{ + krb5_error_code kret; + fd_set mask, readfds; + int nready; +#if POSIX_SIGNALS + struct sigaction s_action; +#endif /* POSIX_SIGNALS */ + + DPRINT(DEBUG_CALLS, net_debug_level, ("* net_dispatch()\n")); + + kret = 0; + + /* Set up the fdset mask */ + FD_ZERO(&mask); + FD_SET(net_listen_socket, &mask); + +#if POSIX_SIGNALS + (void) sigemptyset(&s_action.sa_mask); + s_action.sa_flags = 0; + s_action.sa_handler = net_shutdown; + (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL); +#ifdef DEBUG + (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL); +#endif /* DEBUG */ + if (!detached) + (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL); +#else /* POSIX_SIGNALS */ + /* + * SIGTERM (or SIGINT, if debug, or SIGHUP if not detached) shuts us down. + */ + signal(SIGTERM, net_shutdown); +#ifdef DEBUG + signal(SIGINT, net_shutdown); +#endif /* DEBUG */ + if (!detached) + signal(SIGHUP, net_shutdown); +#endif /* POSIX_SIGNALS */ + +#if !USE_PTHREADS +#if POSIX_SIGNALS + s_action.sa_handler = net_reaper; + (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL); +#else /* POSIX_SIGNALS */ + /* + * SIGCHILD indicates end of child process life. + */ + signal(SIGCHLD, net_reaper); +#endif /* POSIX_SIGNALS */ +#endif /* !USE_PTHREADS */ + + /* Receive connections on the socket */ + DPRINT(DEBUG_OPERATION, net_debug_level, ("+ listening on socket\n")); + if ( +#if POSIX_SETJMP + sigsetjmp(shutdown_jmp, 1) == 0 +#else /* POSIX_SETJMP */ + setjmp(shutdown_jmp) == 0 +#endif /* POSIX_SETJMP */ + ) { + if (listen(net_listen_socket, SOMAXCONN) < 0) + kret = errno; + } + else + kret = EINTR; + DPRINT(DEBUG_OPERATION, net_debug_level, ("+ listen done\n")); + + while (kret == 0) { + /* + * Prepare to catch signals. + */ + if ( +#if POSIX_SETJMP + sigsetjmp(shutdown_jmp, 1) == 0 +#else /* POSIX_SETJMP */ + setjmp(shutdown_jmp) == 0 +#endif /* POSIX_SETJMP */ + ) { + readfds = mask; + DPRINT(DEBUG_OPERATION, net_debug_level, ("+ doing select\n")); + if ((nready = select(net_listen_socket+1, + &readfds, + (fd_set *) NULL, + (fd_set *) NULL, + (struct timeval *) NULL)) == 0) { + DPRINT(DEBUG_OPERATION, net_debug_level, ("+ nobody ready\n")); + continue; /* Nobody ready */ + } + + if ((nready < 0) && (errno != EINTR)) { + com_err(net_dispatch_msg, errno, net_select_fmt); + continue; + } + + if (FD_ISSET(net_listen_socket, &readfds)) { + struct sockaddr_in client_addr; + int addrlen; + int conn_sock; + + addrlen = sizeof(client_addr); + DPRINT(DEBUG_OPERATION, net_debug_level, + ("+ accept connection\n")); + while (((conn_sock = accept(net_listen_socket, + (struct sockaddr *) &client_addr, + &addrlen)) < 0) && + (errno == EINTR)); + + if (conn_sock < 0) { + kret = errno; + break; + } + DPRINT(DEBUG_OPERATION, net_debug_level, + ("+ accepted connection\n")); + kret = net_dispatch_client(kcontext, + net_listen_socket, + conn_sock, + &client_addr); + if (kret) { + com_err(net_dispatch_msg, kret, net_cl_disp_fmt); + continue; + } + DPRINT(DEBUG_OPERATION, net_debug_level, + ("+ dispatch done\n")); + } + else { + com_err(net_dispatch_msg, 0, net_not_ready_fmt); + kret = EIO; + } + } + else { + DPRINT(DEBUG_OPERATION, net_debug_level, + ("+ dispatch interrupted by SIGTERM\n")); + kret = 0; + break; + } + } + + DPRINT(DEBUG_CALLS, net_debug_level, ("X net_dispatch() = %d\n", kret)); + return(kret); +} + +/* + * Return our service principal. + */ +krb5_principal +net_server_princ() +{ + if (net_service_princ_init) + return(net_service_principal); + else + return((krb5_principal) NULL); +}