From: Paul Park Date: Wed, 26 Apr 1995 21:05:11 +0000 (+0000) Subject: New administrative protocol server X-Git-Tag: krb5-1.0-beta5~239 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=67d7479134fd8d635f2f84ae264ce749207fe00f;p=krb5.git New administrative protocol server git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@5510 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/kadmin/v5server/Makefile.in b/src/kadmin/v5server/Makefile.in new file mode 100644 index 000000000..5dd6822e7 --- /dev/null +++ b/src/kadmin/v5server/Makefile.in @@ -0,0 +1,35 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) +LDFLAGS = -g + +COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a +SSLIB=$(BUILDTOP)/util/ss/libss.a +DBMLIB= +KDBLIB=$(TOPLIBD)/libkdb5.a + +all:: + +KLIB = $(KDBLIB) $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(SSLIB) $(COMERRLIB) $(DBMLIB) +DEPKLIB = $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(SSLIB) $(COMERRLIB) $(DBMLIB) + +SRCS = \ + $(srcdir)/srv_main.c $(srcdir)/srv_key.c \ + $(srcdir)/srv_acl.c $(srcdir)/srv_output.c $(srcdir)/srv_net.c \ + $(srcdir)/proto_serv.c $(srcdir)/passwd.c + +OBJS = \ + srv_main.o srv_key.o srv_acl.o srv_output.o srv_net.o \ + proto_serv.o passwd.o + + +all:: kadmind5 + +kadmind5: $(KDBDEPLIB) $(OBJS) $(DEPKLIB) + $(CC) $(CFLAGS) -o kadmind5 $(OBJS) $(KLIB) $(LIBS) + +install:: + $(INSTALL_PROGRAM) kadmind5 ${DESTDIR}$(CLIENT_BINDIR)/kadmind5 + $(INSTALL_DATA) $(srcdir)/kadmind5.M ${DESTDIR}$(CLIENT_MANDIR)/kadmind.8 + +clean:: + $(RM) kadmind5 + diff --git a/src/kadmin/v5server/configure.in b/src/kadmin/v5server/configure.in new file mode 100644 index 000000000..24dc436b7 --- /dev/null +++ b/src/kadmin/v5server/configure.in @@ -0,0 +1,12 @@ +AC_INIT(srv_main.c) +WITH_CCOPTS +CONFIG_RULES +AC_SET_BUILDTOP +AC_PROG_INSTALL +WITH_NETLIB +AC_FUNC_CHECK(waitpid,AC_DEFINE(HAVE_WAITPID)) +CHECK_WAIT_TYPE +ET_RULES +KRB_INCLUDE +WITH_KRB5ROOT +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/v5server/kadm5_defs.h b/src/kadmin/v5server/kadm5_defs.h new file mode 100644 index 000000000..8b2ea5fb8 --- /dev/null +++ b/src/kadmin/v5server/kadm5_defs.h @@ -0,0 +1,195 @@ +/* + * 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 +#ifdef DEBUG +#define DPRINT(l1, cl, al) if ((cl & l1) != 0) printf 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_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) +/* + * Inter-module function prototypes + */ + +/* srv_key.c */ +krb5_error_code key_init + PROTOTYPE((krb5_context, + int, + int, + int, + char *, + int, + char *, + char *)); +void key_finish + PROTOTYPE((krb5_context, + int)); +krb5_error_code key_string_to_keys + PROTOTYPE((krb5_context, + krb5_principal, + krb5_data *, + krb5_int32, + krb5_int32, + krb5_keyblock *, + krb5_keyblock *)); +krb5_error_code key_encrypt_keys + PROTOTYPE((krb5_context, + krb5_principal, + krb5_keyblock *, + krb5_keyblock *, + krb5_encrypted_keyblock *, + krb5_encrypted_keyblock *)); +krb5_error_code key_decrypt_keys + PROTOTYPE((krb5_context, + krb5_principal, + krb5_encrypted_keyblock *, + krb5_encrypted_keyblock *, + krb5_keyblock *, + krb5_keyblock *)); +krb5_boolean key_pwd_is_weak + PROTOTYPE((krb5_context, + krb5_principal, + krb5_data *, + krb5_int32, + krb5_int32)); + +/* srv_acl.c */ +krb5_error_code acl_init + PROTOTYPE((krb5_context, + int, + char *)); +void acl_finish + PROTOTYPE((krb5_context, + int)); +krb5_boolean acl_op_permitted + PROTOTYPE((krb5_context, + krb5_principal, + krb5_int32)); + +/* srv_output.c */ +krb5_error_code output_init + PROTOTYPE((krb5_context, + int, + char *, + krb5_boolean)); +void output_finish + PROTOTYPE((krb5_context, + int)); +krb5_boolean output_lang_supported + PROTOTYPE((char *)); +char *output_krb5_errmsg + PROTOTYPE((char *, + krb5_boolean, + krb5_int32)); +char *output_adm_error + PROTOTYPE((char *, + krb5_boolean, + krb5_int32, + krb5_int32, + krb5_int32, + krb5_data *)); + +/* srv_net.c */ +krb5_error_code net_init + PROTOTYPE((krb5_context, + int)); +void net_finish + PROTOTYPE((krb5_context, + int)); +krb5_error_code net_dispatch + PROTOTYPE((krb5_context)); +krb5_principal net_server_princ(); + +/* proto_serv.c */ +krb5_error_code proto_init + PROTOTYPE((krb5_context, + int, + int)); +void proto_finish + PROTOTYPE((krb5_context, + int)); +krb5_error_code proto_serv + PROTOTYPE((krb5_context, + krb5_int32, + int, + void *, + void *)); + +/* passwd.c */ +krb5_int32 passwd_check + PROTOTYPE((krb5_context, + int, + krb5_auth_context *, + krb5_ticket *, + krb5_data *, + krb5_int32 *)); +krb5_int32 passwd_change + PROTOTYPE((krb5_context, + int, + krb5_auth_context *, + krb5_ticket *, + krb5_data *, + krb5_data *, + krb5_int32 *)); + +#endif /* KADM5_DEFS_H__ */ diff --git a/src/kadmin/v5server/kadmind5.M b/src/kadmin/v5server/kadmind5.M new file mode 100644 index 000000000..b5d7dd4aa --- /dev/null +++ b/src/kadmin/v5server/kadmind5.M @@ -0,0 +1,207 @@ +.\" $Source$ +.\" $Author$ +.\" $Id$ +.\" +.\" Copyright 1995 by the Massachusetts Institute of Technology. +.\" +.\" 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. +.\" +.\" +.TH KADMIND5 1 "Kerberos Version 5.0" "MIT Project Athena" +.SH NAME +kadmind5 \- network daemon for Kerberos version 5 database information +.SH SYNOPSIS +.B kadmind5 +[ +.B \-i +] [ +.B \-a +aclfile +] [ +.B \-d +dbname +] [ +.B \-e +enctype +] [ +.B \-k +mkeytype +] [ +.B \-l +langlist +] [ +.B \-m +1|0 +] [ +.B \-t +timeout +] [ +.B \-D +debugmask +] [ +.B \-M +mkeyname +] +.SH DESCRIPTION +.I kadmind5 +is the network database server for the Kerberos version 5 +password-changing and administration tools. + +.SH FLAGS +.PP +.B Database, Key and Realm flags +.IP \-r +.B realm +specifies the realm that this server is to administer. The default is +the local realm. +.IP \-d +.B dbname +specifies the location of the database. +.IP \-M +.B mkeyname +specifies the name of the master key. +.IP \-k +.B mkeytype +specifies the master key type. +.IP \-i +Indicates that the master key name is to be entered interactively. +.IP \-e +.B enctype +specifies the encryption type which is to be used. +.PP +.B ACL flag +.IP \-a +.B aclfile +specifies the location of the ACL file. This file controls remote +principals' abilities to perform administrative functions. See the +ACL FILE section below for the format of this file. +.PP +.B Connection flag +.IP \-t +Indicates that the server is to terminate a connection if it remains +inactive for +.B timeout +seconds. +.PP +.B Debugging flag +.IP \-D +Enables certain debugging features and messages selected by +.B debugmask. +.PP +.B Protocol Message flags +.IP \-l +.B langlist +specifies the list of supported language names [not implemented]. +.IP \-m +Enables ( +.B 1 +) or disables ( +.B 0 +) MIME encoding support [not implemented]. + +.SH ACL FILE +.PP +The ACL file controls which principals can or cannot perform which +administrative functions. This file can contain comment lines, null +lines or lines which contain ACL entries. Comment lines start with +the sharp sign ( +.B \# +) and continue until the end of the line. Lines containing ACL +entries have the format of +.B principal +.I whitespace +.B operation-mask. +Ordering is important. The first matching entry is the one which will +control access for a particular principal. +.PP +.IP principal +may specify a partially or fully qualified Kerberos version 5 +principal name. Each component of the name may be wildcarded using +the asterick ( +.B * +) character. +.IP operation-mask +Specifies what operations may or may not be peformed by a principal +matching a particular entry. This is a string of one or more of the +following list of characters or their upper-case counterparts. If the +character is upper-case, then the operation is disallowed. If the +character is lower-case, then the operation is permitted. +.TP i +.I a +[Dis]allows the addition of principals from the database. +.TP i +.I d +[Dis]allows the deletion of principals from the database. +.TP i +.I m +[Dis]allows the modification of principals in the database. +.TP i +.I c +[Dis]allows the changing of passwords for principals in the database. +.TP i +.I o +[Dis]allows the changing of the principal's own password in the +database. +.TP i +.I i +[Dis]allows inquiries to the database. +.TP i +.I p +Short for +.I adm. +.TP i +.I w +Short for +.I pw. +.TP i +.I x or * +Short for +.I admcoi. +.PP +Some examples of valid entries here are: +.TP 2i +.I user/instance@realm po +A standard fully qualified name. The +.B operation-mask +only applies to this principal and specifies that [s]he may add, +delete or modify principals and change his/her own password, but not +anybody elses. +.TP 2i +.I user/*@realm aw +A wildcarded name. The +.B operation-mask +applies to all principals in realm "realm" whose first component is +"user" and specifies that [s]he may add principals and change anybody +else's password or change his/her own. +.TP 2i +.I * o +A catchall entry. The +.B operation-mask +applies to all principals and indicates that they may change their own +passwords. + +.SH FILES +.TP 2i +/krb5/principal.* +the default location of the database. +.TP 2i +/etc/krb5_adm.acl +the default location of the ACL file. +.SH SEE ALSO +kpasswd(1), kadmin5(8) +.SH BUGS + diff --git a/src/kadmin/v5server/passwd.c b/src/kadmin/v5server/passwd.c new file mode 100644 index 000000000..0dea07da1 --- /dev/null +++ b/src/kadmin/v5server/passwd.c @@ -0,0 +1,500 @@ +/* + * kadmin/v5server/passwd.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. + * + */ + +/* + * passwd.c - handle server password-related functions. + */ + +#include "k5-int.h" +#include "com_err.h" +#include "kadm5_defs.h" +#include "adm.h" + +/* + * These defines turn on various checking in passwd_check_npass_ok. + */ +#define KPWD_CHECK_LENGTH 1 +#define KPWD_CHECK_WEAKNESS 1 + +#define KPWD_MIN_PWD_LENGTH 8 + +extern char *programname; +static const char *pwd_bad_old_pwd = "incorrect old password for %s"; +static const char *pwd_perm_denied = "ACL entry prevents password change for %s"; +static const char *pwd_changed_pwd = "changed password for %s"; + +/* + * passwd_check_princ() - Check if the principal specified in the ticket is ok + */ +static krb5_error_code +passwd_check_princ(kcontext, debug_level, ticket, + princp, namep, db_entp, db_numentp, db_morep) + krb5_context kcontext; + int debug_level; + krb5_ticket *ticket; + krb5_principal *princp; + char **namep; + krb5_db_entry *db_entp; + int *db_numentp; + krb5_boolean *db_morep; +{ + krb5_error_code kret; + + DPRINT(DEBUG_CALLS, debug_level, ("* passwd_check_princ()\n")); + *princp = (krb5_principal) NULL; + *namep = (char *) NULL; + + /* Copy principal out of ticket */ + if (kret = krb5_copy_principal(kcontext, + ticket->enc_part2->client, + princp)) + goto cleanup; + + /* Flatten name */ + if (kret = krb5_unparse_name(kcontext, *princp, namep)) + goto cleanup; + + /* Get database entry */ + if (kret = krb5_db_get_principal(kcontext, + *princp, + db_entp, + db_numentp, + db_morep)) + goto cleanup; + + if (*db_numentp == 0) + kret = KRB5_KDB_NOENTRY; + + cleanup: + if (kret) { + if (*namep) { + krb5_xfree(*namep); + *namep = (char *) NULL; + } + if (*princp) { + krb5_free_principal(kcontext, *princp); + *princp = (krb5_principal) NULL; + } + } + + DPRINT(DEBUG_CALLS, debug_level, ("X passwd_check_princ() = %d\n", kret)); + return(kret); +} + +/* + * passwd_check_opass_ok() - Check of specified old password is good. + */ +static krb5_boolean +passwd_check_opass_ok(kcontext, debug_level, princ, dbentp, pwdata) + krb5_context kcontext; + int debug_level; + krb5_principal princ; + krb5_db_entry *dbentp; + krb5_data *pwdata; +{ + krb5_boolean pwret; + krb5_keyblock pkey, akey; + krb5_keyblock pkey1, akey1; + krb5_error_code kret; + + DPRINT(DEBUG_CALLS, debug_level, ("* passwd_check_opass_ok()\n")); + pwret = 1; + + /* Initialize */ + memset((char *) &pkey, 0, sizeof(pkey)); + memset((char *) &akey, 0, sizeof(akey)); + memset((char *) &pkey1, 0, sizeof(pkey)); + memset((char *) &akey1, 0, sizeof(akey)); + + /* Make key(s) using alleged old password */ + kret = key_string_to_keys(kcontext, + princ, + pwdata, + dbentp->salt_type, + dbentp->alt_salt_type, + &pkey, + &akey); + + /* Now decrypt database entries */ + if (!kret) + kret = key_decrypt_keys(kcontext, princ, + &dbentp->key, &dbentp->alt_key, + &pkey1, &akey1); + if (kret) + goto cleanup; + + /* + * Compare decrypted keys. If they differ, then we're wrong! + */ + if ((pkey1.length && (pkey.length != pkey1.length)) || + (akey1.length && (akey.length != akey1.length)) || + (pkey1.length && memcmp(pkey1.contents, + pkey.contents, + pkey.length)) || + (akey1.length && memcmp(akey1.contents, + akey.contents, + akey.length))) + pwret = 0; + + cleanup: + if (kret) + pwret = 0; + if (akey1.contents) { + memset((char *) akey1.contents, 0, akey1.length); + krb5_xfree(akey1.contents); + } + if (pkey1.contents) { + memset((char *) pkey1.contents, 0, pkey1.length); + krb5_xfree(pkey1.contents); + } + if (akey.contents) { + memset((char *) akey.contents, 0, akey.length); + krb5_xfree(akey.contents); + } + if (pkey.contents) { + memset((char *) pkey.contents, 0, pkey.length); + krb5_xfree(pkey.contents); + } + DPRINT(DEBUG_CALLS, debug_level, + ("X passwd_check_opass_ok() = %d\n", pwret)); + return(pwret); +} + +/* + * passwd_check_npass_ok() - Check if new password is ok. + */ +static krb5_boolean +passwd_check_npass_ok(kcontext, debug_level, princ, dbentp, pwdata, supp) + krb5_context kcontext; + int debug_level; + krb5_principal princ; + krb5_db_entry *dbentp; + krb5_data *pwdata; + krb5_int32 *supp; +{ + krb5_boolean pwret; + + DPRINT(DEBUG_CALLS, debug_level, ("* passwd_check_npass_ok()\n")); + pwret = 1; + + /* + * Check whether a new password is good. + */ +#if KPWD_CHECK_LENGTH + /* Check length */ + if (pwdata->length < KPWD_MIN_PWD_LENGTH) { + pwret = 0; + *supp = KRB5_ADM_PWD_TOO_SHORT; + DPRINT(DEBUG_CALLS, debug_level, + ("* passwd_check_npass_ok() - TOO SHORT\n")); + } +#endif /* KPWD_CHECK_LENGTH */ + +#if KPWD_CHECK_WEAKNESS + /* Check weakness of keys generated by password */ + if (key_pwd_is_weak(kcontext, + princ, + pwdata, + dbentp->salt_type, + dbentp->alt_salt_type)) { + pwret = 0; + *supp = KRB5_ADM_PWD_WEAK; + DPRINT(DEBUG_CALLS, debug_level, + ("* passwd_check_npass_ok() - WEAK\n")); + } +#endif /* KPWD_CHECK_WEAKNESS */ + + DPRINT(DEBUG_CALLS, debug_level, + ("X passwd_check_npass_ok() = %d\n", pwret)); + return(pwret); +} + +/* + * passwd_set_npass() - Set new password + */ +static krb5_error_code +passwd_set_npass(kcontext, debug_level, princ, dbentp, pwdata) + krb5_context kcontext; + int debug_level; + krb5_principal princ; + krb5_db_entry *dbentp; + krb5_data *pwdata; +{ + krb5_keyblock pkey, akey; + krb5_error_code kret; + krb5_db_entry entry2write; + int nwrite; + + DPRINT(DEBUG_CALLS, debug_level, ("* passwd_set_npass()\n")); + + /* Initialize */ + memset((char *) &pkey, 0, sizeof(pkey)); + memset((char *) &akey, 0, sizeof(akey)); + memset((char *) &entry2write, 0, sizeof(krb5_db_entry)); + + /* Make key(s) using the new password */ + if (kret = key_string_to_keys(kcontext, + princ, + pwdata, + dbentp->salt_type, + dbentp->alt_salt_type, + &pkey, + &akey)) + goto cleanup; + + /* Now get a new database entry */ + memcpy((char *) &entry2write, (char *) dbentp, sizeof(krb5_db_entry)); + memset((char *) &entry2write.key, 0, sizeof(krb5_encrypted_keyblock)); + memset((char *) &entry2write.alt_key, 0, sizeof(krb5_encrypted_keyblock)); + + /* Encrypt the new keys to the database entry. */ + if (kret = key_encrypt_keys(kcontext, + princ, + &pkey, + &akey, + &entry2write.key, + &entry2write.alt_key)) + goto cleanup; + + /* Set the time */ + if (kret = krb5_timeofday(kcontext, &entry2write.mod_date)) + goto cleanup; + + /* Salt? */ + + /* Now write the entry */ + nwrite = 1; + if (kret = krb5_db_put_principal(kcontext, &entry2write, &nwrite)) + goto cleanup; + + if (nwrite != 1) + kret = KRB_ERR_GENERIC; + + cleanup: + if (entry2write.key.contents) { + memset((char *) &entry2write.key, 0, sizeof(krb5_encrypted_keyblock)); + krb5_xfree(entry2write.key.contents); + } + if (entry2write.alt_key.contents) { + memset((char *) &entry2write.alt_key, 0, + sizeof(krb5_encrypted_keyblock)); + krb5_xfree(entry2write.alt_key.contents); + } + if (akey.contents) { + memset((char *) akey.contents, 0, akey.length); + krb5_xfree(akey.contents); + } + if (pkey.contents) { + memset((char *) pkey.contents, 0, pkey.length); + krb5_xfree(pkey.contents); + } + DPRINT(DEBUG_CALLS, debug_level, + ("X passwd_set_npass() = %d\n", kret)); + return(kret); +} + +/* + * passwd_check() - Check if a password is ok. + */ +krb5_int32 +passwd_check(kcontext, debug_level, auth_context, ticket, pwdata, supp) + krb5_context kcontext; + int debug_level; + krb5_auth_context *auth_context; + krb5_ticket *ticket; + krb5_data *pwdata; + krb5_int32 *supp; +{ + krb5_int32 pwret; + krb5_error_code kret; + krb5_principal client; + char *canon_name; + krb5_db_entry tmp_entry; + int tmp_nents; + int tmp_more; + + DPRINT(DEBUG_CALLS, debug_level, ("* passwd_check()\n")); + pwret = KRB5_ADM_SUCCESS; + client = (krb5_principal) NULL; + canon_name = (char *) NULL; + + /* + * Check out our principal + */ + tmp_nents = 1; + if (kret = passwd_check_princ(kcontext, + debug_level, + ticket, + &client, + &canon_name, + &tmp_entry, + &tmp_nents, + &tmp_more)) { + *supp = KRB5_ADM_BAD_PRINC; + goto cleanup; + } + + DPRINT(DEBUG_REQUESTS, debug_level, + ("> Checking password for client \"%s\"\n", canon_name)); + + /* + * And check out our password. + */ + if (!passwd_check_npass_ok(kcontext, + debug_level, + client, + &tmp_entry, + pwdata, + supp)) + pwret = KRB5_ADM_PW_UNACCEPT; + + cleanup: + if (kret) { + pwret = KRB5_ADM_PW_UNACCEPT; + } + if (tmp_nents > 0) + krb5_db_free_principal(kcontext, &tmp_entry, tmp_nents); + if (canon_name) + krb5_xfree(canon_name); + if (client) + krb5_free_principal(kcontext, client); + + done: + DPRINT(DEBUG_CALLS, debug_level, ("X passwd_check() = %d\n", pwret)); + return(pwret); +} + +/* + * passwd_change() - Change a password. + */ +krb5_int32 +passwd_change(kcontext, debug_level, auth_context, ticket, + olddata, newdata, supp) + krb5_context kcontext; + int debug_level; + krb5_auth_context *auth_context; + krb5_ticket *ticket; + krb5_data *olddata; + krb5_data *newdata; + krb5_int32 *supp; +{ + krb5_int32 pwret; + krb5_error_code kret; + krb5_principal client; + char *canon_name; + krb5_db_entry tmp_entry; + int tmp_nents; + int tmp_more; + + DPRINT(DEBUG_CALLS, debug_level, ("* passwd_change()\n")); + pwret = KRB5_ADM_SUCCESS; + client = (krb5_principal) NULL; + canon_name = (char *) NULL; + + /* Make sure the ticket is initial, otherwise don't trust it */ + if ((ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) { + pwret = KRB5_ADM_NOT_IN_TKT; + goto done; + } + + /* + * Check out our principal + */ + tmp_nents = 1; + if (kret = passwd_check_princ(kcontext, + debug_level, + ticket, + &client, + &canon_name, + &tmp_entry, + &tmp_nents, + &tmp_more)) { + *supp = KRB5_ADM_BAD_PRINC; + goto cleanup; + } + + /* + * Check if we're restricted by an ACL from changing our own password. + */ + if (!acl_op_permitted(kcontext, client, ACL_CHANGE_OWN_PW)) { + com_err(programname, 0, pwd_perm_denied, canon_name); + pwret = KRB5_ADM_CANT_CHANGE; + *supp = KRB5_ADM_NOT_ALLOWED; + goto cleanup; + } + + DPRINT(DEBUG_REQUESTS, debug_level, + ("> Changing password for client \"%s\"\n", canon_name)); + + /* + * Check out our old password. + */ + if (!passwd_check_opass_ok(kcontext, + debug_level, + client, + &tmp_entry, + olddata)) { + com_err(programname, 0, pwd_bad_old_pwd, canon_name); + pwret = KRB5_ADM_BAD_PW; + goto cleanup; + } + + /* + * Check out the new password. + */ + if (!passwd_check_npass_ok(kcontext, + debug_level, + client, + &tmp_entry, + newdata, + supp)) { + pwret = KRB5_ADM_PW_UNACCEPT; + goto cleanup; + } + + /* Now set the new entry */ + kret = passwd_set_npass(kcontext, + debug_level, + client, + &tmp_entry, + newdata); + if (!kret) { + com_err(programname, 0, pwd_changed_pwd, canon_name); + } + + cleanup: + if (kret) { + pwret = KRB5_ADM_CANT_CHANGE; + } + if (tmp_nents > 0) + krb5_db_free_principal(kcontext, &tmp_entry, tmp_nents); + if (canon_name) + krb5_xfree(canon_name); + if (client) + krb5_free_principal(kcontext, client); + + done: + DPRINT(DEBUG_CALLS, debug_level, ("X passwd_change() = %d\n", pwret)); + return(pwret); +} diff --git a/src/kadmin/v5server/proto_serv.c b/src/kadmin/v5server/proto_serv.c new file mode 100644 index 000000000..a49e68956 --- /dev/null +++ b/src/kadmin/v5server/proto_serv.c @@ -0,0 +1,545 @@ +/* + * 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 +#include +#include +#include +#include +#include "k5-int.h" +#include "com_err.h" +#include "kadm5_defs.h" +#include "adm.h" + +static const char *proto_addrs_msg = "%d: cannot get memory for addresses"; +static const char *proto_rcache_msg = "%d: cannot get replay cache"; +static const char *proto_ap_req_msg = "%d: error reading AP_REQ message"; +static const char *proto_auth_con_msg = "%d: cannot get authorization context"; +static const char *proto_rd_req_msg = "%d: cannot decode AP_REQ message"; +static const char *proto_mk_rep_msg = "%d: cannot generate AP_REP message"; +static const char *proto_wr_rep_msg = "%d: cannot write AP_REP message"; +static const char *proto_conn_abort_msg = "%d: connection destroyed by client"; +static const char *proto_seq_err_msg = "%d: protocol sequence violation"; +static const char *proto_rd_cmd_msg = "%d: cannot read administrative protocol command"; +static const char *proto_wr_reply_msg = "%d: cannot write administrative protocol reply"; +static const char *proto_fmt_reply_msg = "%d: cannot format administrative protocol reply"; +extern char *programname; + +static int proto_proto_timeout = -1; +static int proto_debug_level = 0; +static jmp_buf timeout_jmp; + +static krb5_sigtype +proto_alarmclock() +{ + longjmp(timeout_jmp, 1); + /* 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; + + char *curr_lang = (char *) NULL; + krb5_boolean mime_setting = 0; + + krb5_int32 num_args; + krb5_data *arglist; + + 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; + + /* 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(), + (krb5_keytab) NULL, + &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 (setjmp(timeout_jmp) == 0) { + if (proto_proto_timeout > 0) { + signal(SIGALRM, proto_alarmclock); + } + /* + * Loop forever - or until somebody puts us out of our misery. + */ + while (1) { + krb5_int32 cmd_error; + krb5_int32 err_aux; + 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; + + /* + * 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; + err_aux = 0; + if (num_args > 0) { + if (!strcmp(arglist[0].data, KRB5_ADM_QUIT_CMD)) { + 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; + err_aux = KRB5_ADM_BAD_ARGS; + } + } + else if (!strcmp(arglist[0].data, KRB5_ADM_CHECKPW_CMD)) { + 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 = passwd_check(kcontext, + proto_debug_level, + auth_context, + ticket, + &arglist[1], + &err_aux); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHECKPW command syntax BAD\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KRB5_ADM_BAD_ARGS; + } + } + else if (!strcmp(arglist[0].data, KRB5_ADM_CHANGEPW_CMD)) { + 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 = passwd_change(kcontext, + proto_debug_level, + auth_context, + ticket, + &arglist[1], + &arglist[2], + &err_aux); + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:CHANGEPW command syntax BAD\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KRB5_ADM_BAD_ARGS; + } + } + else if (!strcmp(arglist[0].data, KRB5_ADM_MOTD_CMD)) { + 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 = KRB5_ADM_BAD_ARGS; + } + } + else if (!strcmp(arglist[0].data, KRB5_ADM_MIME_CMD)) { + 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 = KRB5_ADM_BAD_ARGS; + } + } + else if (!strcmp(arglist[0].data, KRB5_ADM_LANGUAGE_CMD)) { + 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 = KRB5_ADM_BAD_ARGS; + } + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:UNKNOWN command %s\n", my_id, + arglist[0].data)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KRB5_ADM_BAD_CMD; + } + } + else { + DPRINT(DEBUG_REQUESTS, proto_debug_level, + ("> %d:NO command!\n", my_id)); + cmd_error = KRB5_ADM_CMD_UNKNOWN; + err_aux = KRB5_ADM_NO_CMD; + } + + /* + * 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; + + adm_errmsg = output_adm_error(curr_lang, + mime_setting, + cmd_error, + err_aux, + num_args, + arglist); + if (!adm_errmsg) { + com_err(programname, kret, proto_fmt_reply_msg, my_id); + goto cleanup; + } + reply_comps.data = adm_errmsg; + reply_comps.length = strlen(adm_errmsg); + kret = krb5_send_adm_reply(kcontext, + (krb5_pointer) &cl_sock, + auth_context, + cmd_error, + 1, + &reply_comps); + free(adm_errmsg); + 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 { + DLOG(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 = KRB_ERR_GENERIC; + /* Format the error message in our language */ + errmsg = output_krb5_errmsg(curr_lang, mime_setting, 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); + if (rcache) + krb5_rc_close(kcontext, rcache); + if (auth_context) + krb5_xfree(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); + close(cl_sock); + + done: + DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_serv() = %d\n", kret)); + return(kret); +} diff --git a/src/kadmin/v5server/srv_acl.c b/src/kadmin/v5server/srv_acl.c new file mode 100644 index 000000000..5cca1b0be --- /dev/null +++ b/src/kadmin/v5server/srv_acl.c @@ -0,0 +1,398 @@ +/* + * kadmin/v5server/srv_acl.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_acl.c - Handle Kerberos ACL related functions. + */ +#include +#include +#include +#include "k5-int.h" +#include "com_err.h" +#include "kadm5_defs.h" + +typedef struct _acl_op_table { + char ao_op; + krb5_int32 ao_mask; +} aop_t; + +typedef struct _acl_entry { + struct _acl_entry *ae_next; + char *ae_name; + int ae_name_bad; + krb5_principal ae_principal; + krb5_int32 ae_op_allowed; +} aent_t; + +static const aop_t acl_op_table[] = { + { 'a', ACL_ADD_PRINCIPAL }, + { 'd', ACL_DELETE_PRINCIPAL }, + { 'm', ACL_MODIFY_PRINCIPAL }, + { 'c', ACL_CHANGEPW }, + { 'o', ACL_CHANGE_OWN_PW }, + { 'i', ACL_INQUIRE }, + { 'p', ACL_PRINCIPAL_MASK }, + { 'w', ACL_PASSWD_MASK }, + { 'x', ACL_ALL_MASK }, + { '*', ACL_ALL_MASK }, + { '\0', 0 } +}; + +static aent_t *acl_list_head = (aent_t *) NULL; +static aent_t *acl_list_tail = (aent_t *) NULL; + +static const char *acl_default_file = "/etc/krb5_adm.acl"; +static char *acl_acl_file = (char *) NULL; +static int acl_inited = 0; +static int acl_debug_level = 0; + +static const char *acl_line2long_msg = "%s: line %d too long, truncated\n"; +static const char *acl_op_bad_msg = "Unrecognized ACL operation '%c' in %s\n"; +static const char *acl_syn_err_msg = "%s: syntax error at line %d <%10s...>\n"; +static const char *acl_cantopen_msg = "cannot open ACL file"; + +/* + * acl_get_line() - Get a line from the ACL file. + */ +static char * +acl_get_line(fp, lnp) + FILE *fp; + int *lnp; +{ + int i, domore; + static char acl_buf[BUFSIZ]; + + for (domore = 1; domore && !feof(fp); ) { + /* Copy in the line */ + for (i=0; + ((i + */ + acle = (aent_t *) NULL; + if (sscanf(lp, "%s %s", acle_principal, acle_ops) == 2) { + acle = (aent_t *) malloc(sizeof(aent_t)); + if (acle) { + acle->ae_next = (aent_t *) NULL; + acle->ae_op_allowed = (krb5_int32) 0; + opok = 1; + for (op=acle_ops; *op; op++) { + char rop; + + rop = (isupper(*op)) ? tolower(*op) : *op; + found = 0; + for (t=0; acl_op_table[t].ao_op; t++) { + if (rop == acl_op_table[t].ao_op) { + found = 1; + if (rop == *op) + acle->ae_op_allowed |= acl_op_table[t].ao_mask; + else + acle->ae_op_allowed &= ~acl_op_table[t].ao_mask; + } + } + if (!found) { + fprintf(stderr, acl_op_bad_msg, *op, lp); + opok = 0; + } + } + if (opok) { + acle->ae_name = (char *) malloc(strlen(acle_principal)+1); + if (acle->ae_name) { + strcpy(acle->ae_name, acle_principal); + acle->ae_principal = (krb5_principal) NULL; + acle->ae_name_bad = 0; + DPRINT(DEBUG_ACL, acl_debug_level, + ("A ACL entry %s -> opmask %x\n", + acle->ae_name, acle->ae_op_allowed)); + } + else { + free(acle); + acle = (aent_t *) NULL; + } + } + else { + free(acle); + acle = (aent_t *) NULL; + } + } + } + DPRINT(DEBUG_CALLS, acl_debug_level, + ("X acl_parse_line() = %x\n", (long) acle)); + return(acle); +} + +/* + * acl_free_entries() - Free all ACL entries. + */ +static void +acl_free_entries() +{ + aent_t *ap; + + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_free_entries()\n")); + for (ap=acl_list_head; ap; ap = ap->ae_next) { + if (ap->ae_name) + free(ap->ae_name); + if (ap->ae_principal) + krb5_free_principal((krb5_context) NULL, ap->ae_principal); + free(ap); + } + acl_list_head = acl_list_tail = (aent_t *) NULL; + acl_inited = 0; + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_free_entries()\n")); +} + +/* + * acl_load_acl_file() - Open and parse the ACL file. + */ +static int +acl_load_acl_file() +{ + FILE *afp; + char *alinep; + aent_t **aentpp; + int alineno; + int retval = 1; + + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_load_acl_file()\n")); + /* Open the ACL file for read */ + if (afp = fopen(acl_acl_file, "r")) { + alineno = 1; + aentpp = &acl_list_head; + + /* Get a non-comment line */ + while (alinep = acl_get_line(afp, &alineno)) { + /* Parse it */ + *aentpp = acl_parse_line(alinep); + /* If syntax error, then fall out */ + if (!*aentpp) { + fprintf(stderr, acl_syn_err_msg, + acl_acl_file, alineno, alinep); + retval = 0; + break; + } + acl_list_tail = *aentpp; + aentpp = &(*aentpp)->ae_next; + } + fclose(afp); + } + else { + com_err(acl_acl_file, errno, acl_cantopen_msg); + } + + if (!retval) { + acl_free_entries(); + } + DPRINT(DEBUG_CALLS, acl_debug_level, + ("X acl_load_acl_file() = %d\n", retval)); + return(retval); +} + +/* + * acl_reload_acl_file() - Reload the acl file. + */ +static krb5_sigtype +acl_reload_acl_file() +{ + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_reload_acl_file()\n")); + acl_free_entries(); + acl_inited = acl_load_acl_file(); + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_reload_acl_file()\n")); +} + +/* + * acl_match_data() - See if two data entries match. + * + * Wildcarding is only supported for a whole component. + */ +static krb5_boolean +acl_match_data(e1, e2) + krb5_data *e1, *e2; +{ + krb5_boolean retval; + + DPRINT(DEBUG_CALLS, acl_debug_level, + ("* acl_match_entry(%s, %s)\n", e1->data, e2->data)); + retval = 0; + if (!strncmp(e1->data, "*", e1->length) || + !strncmp(e2->data, "*", e2->length)) { + retval = 1; + } + else { + if ((e1->length == e2->length) && + (!strncmp(e1->data, e2->data, e1->length))) + retval = 1; + } + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval)); + return(retval); +} + +/* + * acl_find_entry() - Find a matching entry. + */ +static aent_t * +acl_find_entry(kcontext, principal) + krb5_context kcontext; + krb5_principal principal; +{ + aent_t *entry; + krb5_error_code kret; + int i; + int matchgood; + + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_find_entry()\n")); + for (entry=acl_list_head; entry; entry = entry->ae_next) { + if (!strcmp(entry->ae_name, "*")) { + DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n")); + break; + } + if (!entry->ae_principal && !entry->ae_name_bad) { + kret = krb5_parse_name(kcontext, + entry->ae_name, + &entry->ae_principal); + if (kret) + entry->ae_name_bad = 1; + } + if (entry->ae_name_bad) { + DPRINT(DEBUG_ACL, acl_debug_level, + ("A Bad ACL entry %s\n", entry->ae_name)); + continue; + } + matchgood = 0; + if (acl_match_data(&entry->ae_principal->realm, + &principal->realm) && + (entry->ae_principal->length == principal->length)) { + matchgood = 1; + for (i=0; ilength; i++) { + if (!acl_match_data(&entry->ae_principal->data[i], + &principal->data[i])) { + matchgood = 0; + break; + } + } + } + if (matchgood) + break; + } + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_find_entry()=%x\n",entry)); + return(entry); +} + +/* + * acl_init() - Initialize ACL context. + */ +krb5_error_code +acl_init(kcontext, debug_level, acl_file) + krb5_context kcontext; + int debug_level; + char *acl_file; +{ + krb5_error_code kret; + + kret = 0; + acl_debug_level = debug_level; + DPRINT(DEBUG_CALLS, acl_debug_level, + ("* acl_init(afile=%s)\n", + ((acl_file) ? acl_file : "(null)"))); + acl_acl_file = (acl_file) ? acl_file : acl_default_file; + acl_inited = acl_load_acl_file(); + signal(SIGHUP, acl_reload_acl_file); + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_init() = %d\n", kret)); + return(kret); +} + +/* + * acl_finish - Terminate ACL context. + */ +void +acl_finish(kcontext, debug_level) + krb5_context kcontext; + int debug_level; +{ + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_finish()\n")); + acl_free_entries(); + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_finish()\n")); +} + +/* + * acl_op_permitted() - Is this operation permitted for this principal? + */ +krb5_boolean +acl_op_permitted(kcontext, principal, opmask) + krb5_context kcontext; + krb5_principal principal; + krb5_int32 opmask; +{ + krb5_boolean retval; + aent_t *aentry; + + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n")); + retval = 1; + if (aentry = acl_find_entry(kcontext, principal)) { + if ((aentry->ae_op_allowed & opmask) != opmask) + retval = 0; + } + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n", + retval)); + return(retval); +} diff --git a/src/kadmin/v5server/srv_key.c b/src/kadmin/v5server/srv_key.c new file mode 100644 index 000000000..8a3055a9a --- /dev/null +++ b/src/kadmin/v5server/srv_key.c @@ -0,0 +1,527 @@ +/* + * kadmin/v5server/srv_key.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_key.c - Handle Kerberos key related functions. + */ +#include "k5-int.h" +#include "com_err.h" +#include "kadm5_defs.h" +#include "mit-des.h" + +static const char *key_bad_etype_fmt = "%s: bad etype %d (%s).\n"; +static const char *key_def_realm_fmt = "%s: cannot find default realm (%s).\n"; +static const char *key_setup_mkey_fmt = "%s: cannot setup master key name (%s).\n"; +static const char *key_get_mkey_fmt = "%s: cannot retrieve master key (%s).\n"; +static const char *key_vfy_mkey_fmt = "%s: cannot verify master key (%s).\n"; +static const char *key_proc_mkey_fmt = "%s: cannot process master key (%s).\n"; +static const char *key_kgen_initf_fmt = "%s: disabling key type %d because initialization failed (%s).\n"; +static const char *key_bad_name_fmt = "%s: cannot set database name to %s (%s).\n"; +static const char *key_cant_init_fmt = "%s: cannot initialize database (%s).\n"; +static const char *key_vmast_key_fmt = "%s: cannot verify master key (%s).\n"; +static const char *key_key_pp_fmt = "%s: cannot preprocess key (%s).\n"; +static const char *key_getm_fmt = "%s: cannot get master entry (%s).\n"; + +static int mprinc_init = 0; +static krb5_principal master_principal; + +static int mkeyb_init = 0; +static krb5_keyblock master_keyblock; + +static int mencb_init = 0; +static krb5_encrypt_block master_encblock; + +static int mrand_init = 0; +static krb5_pointer master_random; + +static int ment_init = 0; +static krb5_db_entry master_entry; + +static int key_debug_level = 0; + +extern char *programname; + +/* + * key_init() - Initialize key context. + */ +krb5_error_code +key_init(kcontext, debug_level, enc_type, key_type, master_key_name, manual, + db_file, db_realm) + krb5_context kcontext; + int debug_level; + int enc_type; + int key_type; + char *master_key_name; + int manual; + char *db_file; + char *db_realm; +{ + krb5_enctype kdc_etype; + char *mkey_name; + char *the_realm; + + krb5_error_code kret; + krb5_enctype etype; + int one_success; + int number_of_entries; + krb5_boolean more_entries; + + key_debug_level = debug_level; + DPRINT(DEBUG_CALLS, key_debug_level, + ("* key_init(enc-type=%d, key-type=%d,\n\tmkeyname=%s, manual=%d,\n\tdb=%s, realm=%s)\n", + enc_type, key_type, + ((master_key_name) ? master_key_name : "(null)"), + manual, + ((db_file) ? db_file : "(default)"), + ((db_realm) ? db_realm : "(null)"))); + /* + * Figure out arguments. + */ + master_keyblock.keytype = ((key_type == -1) ? KEYTYPE_DES : key_type); + mkey_name = ((!master_key_name) ? KRB5_KDB_M_NAME : master_key_name); + kdc_etype = ((enc_type == -1) ? DEFAULT_KDC_ETYPE : enc_type); + if (!valid_etype(kdc_etype)) { + kret = KRB5_PROG_ETYPE_NOSUPP; + fprintf(stderr, key_bad_etype_fmt, programname, kdc_etype, + error_message(kret)); + goto leave; + } + if (!db_realm) { + kret = krb5_get_default_realm(kcontext, &the_realm); + if (kret) { + fprintf(stderr, key_def_realm_fmt, programname, + error_message(kret)); + goto leave; + } + } + else + the_realm = db_realm; + DPRINT(DEBUG_REALM, key_debug_level, + ("- initializing for realm %s\n", the_realm)); + + /* Set database name if supplied */ + if (db_file && (kret = krb5_db_set_name(kcontext, db_file))) { + fprintf(stderr, key_bad_name_fmt, programname, db_file, + error_message(kret)); + goto leave; + } + + /* Initialize database */ + if (kret = krb5_db_init(kcontext)) { + fprintf(stderr, key_cant_init_fmt, programname, + error_message(kret)); + goto leave; + } + + /* Assemble and parse the master key name */ + kret = krb5_db_setup_mkey_name(kcontext, + mkey_name, + the_realm, + (char **) NULL, + &master_principal); + if (kret) { + fprintf(stderr, key_setup_mkey_fmt, programname, + error_message(kret)); + goto cleanup; + } + mprinc_init = 1; + DPRINT(DEBUG_HOST, key_debug_level, + ("- master key is %s@%s\n", mkey_name, the_realm)); + + /* Get the master database entry and save it. */ + number_of_entries = 1; + kret = krb5_db_get_principal(kcontext, + master_principal, + &master_entry, + &number_of_entries, + &more_entries); + if (!kret) { + if (number_of_entries != 1) { + if (number_of_entries) + krb5_db_free_principal(kcontext, + &master_entry, + number_of_entries); + kret = KRB5_KDB_NOMASTERKEY; + } + else if (more_entries) { + krb5_db_free_principal(kcontext, + &master_entry, + number_of_entries); + kret = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; + } + } + if (kret) { + fprintf(stderr, key_getm_fmt, programname, error_message(kret)); + goto leave; + } + ment_init = 1; + + krb5_use_cstype(kcontext, &master_encblock, kdc_etype); + mencb_init = 1; + + /* Go get the master key */ + kret = krb5_db_fetch_mkey(kcontext, + master_principal, + &master_encblock, + manual, + FALSE, /* Only read once if manual */ + 0, /* No salt */ + &master_keyblock); + if (kret) { + fprintf(stderr, key_get_mkey_fmt, programname, + error_message(kret)); + goto cleanup; + } + mkeyb_init = 1; + + /* Verify the master key */ + if (kret = krb5_db_verify_master_key(kcontext, + master_principal, + &master_keyblock, + &master_encblock)) { + fprintf(stderr, key_vmast_key_fmt, programname, + error_message(kret)); + goto leave; + } + + /* Do any key pre-processing */ + if (kret = krb5_process_key(kcontext, + &master_encblock, + &master_keyblock)) { + fprintf(stderr, key_key_pp_fmt, programname, error_message(kret)); + goto leave; + } + + /* Now initialize the random key */ + kret = krb5_init_random_key(kcontext, + &master_encblock, + &master_keyblock, + &master_random); + if (!kret) + mrand_init = 1; + + cleanup: + if (kret) { + if (mrand_init) { + krb5_finish_random_key(kcontext, &master_encblock, &master_random); + mrand_init = 0; + } + if (mencb_init) { + krb5_finish_key(kcontext, &master_encblock); + mencb_init = 0; + } + if (mkeyb_init) { + memset(master_keyblock.contents, 0, master_keyblock.length); + krb5_xfree(master_keyblock.contents); + mkeyb_init = 0; + } + if (ment_init) { + krb5_db_free_principal(kcontext, &master_entry, 1); + ment_init = 0; + } + } + if (the_realm != db_realm) + krb5_xfree(the_realm); + leave: + DPRINT(DEBUG_CALLS, key_debug_level, ("X key_init() = %d\n", kret)); + return(kret); +} + +/* + * key_finish - Terminate key context. + */ +void +key_finish(kcontext, debug_level) + krb5_context kcontext; + int debug_level; +{ + DPRINT(DEBUG_CALLS, key_debug_level, ("* key_finish()\n")); + if (mrand_init) { + krb5_finish_random_key(kcontext, &master_encblock, &master_random); + mrand_init = 0; + } + if (mkeyb_init) { + krb5_finish_key(kcontext, &master_encblock); + memset((char *) &master_encblock, 0, + sizeof(master_encblock)); + mkeyb_init = 0; + } + if (mprinc_init) { + krb5_free_principal(kcontext, master_principal); + mprinc_init = 0; + } + if (mencb_init) { + master_encblock.crypto_entry = 0; + mencb_init = 0; + } + if (ment_init) { + krb5_db_free_principal(kcontext, &master_entry, 1); + ment_init = 0; + } + krb5_db_fini(kcontext); + /* memset((char *) tgs_key.contents, 0, tgs_key.length); */ + DPRINT(DEBUG_CALLS, key_debug_level, ("X key_finish()\n")); +} + +/* + * key_string_to_keys() - convert string to keys. + */ +krb5_error_code +key_string_to_keys(kcontext, principal, string, psalttype, asalttype, + primary, alternate) + krb5_context kcontext; + krb5_principal principal; + krb5_data *string; + krb5_int32 psalttype; + krb5_int32 asalttype; + krb5_keyblock *primary; + krb5_keyblock *alternate; +{ + krb5_error_code kret; + krb5_data psalt_data, asalt_data; + + DPRINT(DEBUG_CALLS, key_debug_level, ("* key_string_to_keys()\n")); + kret = KRB_ERR_GENERIC; + psalt_data.length = asalt_data.length = 0; + psalt_data.data = asalt_data.data = (char *) NULL; + + /* + * Determine the primary salt type. + */ + switch (psalttype) { + case KRB5_KDB_SALTTYPE_NORMAL: + /* Normal salt */ + if (kret = krb5_principal2salt(kcontext, principal, &psalt_data)) + goto done; + asalt_data.data = (char *) NULL; + asalt_data.length = 0; + break; + case KRB5_KDB_SALTTYPE_V4: + /* V4 salt */ + psalt_data.data = (char *) NULL; + psalt_data.length = 0; + if (kret = krb5_principal2salt(kcontext, principal, &asalt_data)) + goto done; + break; + case KRB5_KDB_SALTTYPE_NOREALM: + if (kret = krb5_principal2salt_norealm(kcontext, + principal, + &psalt_data)) + goto done; + asalt_data.data = (char *) NULL; + asalt_data.length = 0; + break; + case KRB5_KDB_SALTTYPE_ONLYREALM: + { + krb5_data *tmp; + + if (kret = krb5_copy_data(kcontext, + krb5_princ_realm(kcontext, principal), + &tmp)) + goto done; + psalt_data = *tmp; + krb5_xfree(tmp); + asalt_data.data = (char *) NULL; + asalt_data.length = 0; + break; + } + default: + goto done; + } + + /* Now convert the string to keys */ + kret = krb5_string_to_key(kcontext, + &master_encblock, + master_keyblock.keytype, + primary, + string, + &psalt_data); + if (!kret) + kret = krb5_string_to_key(kcontext, + &master_encblock, + master_keyblock.keytype, + alternate, + string, + &asalt_data); + + done: + if (kret) { + if (primary->contents) { + memset((char *) primary->contents, 0, primary->length); + krb5_xfree(primary->contents); + } + if (alternate->contents) { + memset((char *) alternate->contents, 0, alternate->length); + krb5_xfree(alternate->contents); + } + } + if (psalt_data.data) { + memset(psalt_data.data, 0, psalt_data.length); + krb5_xfree(psalt_data.data); + } + if (asalt_data.data) { + memset(asalt_data.data, 0, asalt_data.length); + krb5_xfree(asalt_data.data); + } + DPRINT(DEBUG_CALLS, key_debug_level, + ("X key_string_to_keys() = %d\n", kret)); + return(kret); +} + +/* + * key_encrypt_keys() - encrypt keys. + */ +krb5_error_code +key_encrypt_keys(kcontext, principal, primary, alternate, eprimary, ealternate) + krb5_context kcontext; + krb5_principal principal; + krb5_keyblock *primary; + krb5_keyblock *alternate; + krb5_encrypted_keyblock *eprimary; + krb5_encrypted_keyblock *ealternate; +{ + krb5_error_code kret; + + DPRINT(DEBUG_CALLS, key_debug_level, ("* key_encrypt_keys()\n")); + + kret = krb5_kdb_encrypt_key(kcontext, + &master_encblock, + primary, + eprimary); + if (kret) + kret = krb5_kdb_encrypt_key(kcontext, + &master_encblock, + alternate, + ealternate); + done: + if (kret) { + if (eprimary->contents) { + memset((char *) eprimary->contents, 0, eprimary->length); + krb5_xfree(eprimary->contents); + } + if (ealternate->contents) { + memset((char *) ealternate->contents, 0, ealternate->length); + krb5_xfree(ealternate->contents); + } + } + DPRINT(DEBUG_CALLS, key_debug_level, + ("X key_encrypt_keys() = %d\n", kret)); + return(kret); +} + +/* + * key_decrypt_keys() - decrypt keys. + */ +krb5_error_code +key_decrypt_keys(kcontext, principal, eprimary, ealternate, primary, alternate) + krb5_context kcontext; + krb5_principal principal; + krb5_encrypted_keyblock *eprimary; + krb5_encrypted_keyblock *ealternate; + krb5_keyblock *primary; + krb5_keyblock *alternate; +{ + krb5_error_code kret; + + DPRINT(DEBUG_CALLS, key_debug_level, ("* key_decrypt_keys()\n")); + + kret = krb5_kdb_decrypt_key(kcontext, + &master_encblock, + eprimary, + primary); + if (kret) + kret = krb5_kdb_decrypt_key(kcontext, + &master_encblock, + ealternate, + alternate); + done: + if (kret) { + if (primary->contents) { + memset((char *) primary->contents, 0, primary->length); + krb5_xfree(primary->contents); + } + if (alternate->contents) { + memset((char *) alternate->contents, 0, alternate->length); + krb5_xfree(alternate->contents); + } + } + DPRINT(DEBUG_CALLS, key_debug_level, + ("X key_decrypt_keys() = %d\n", kret)); + return(kret); +} + +/* + * key_pwd_is_weak() - Check for weakness of key from password + */ +krb5_boolean +key_pwd_is_weak(kcontext, principal, string, psalttype, asalttype) + krb5_context kcontext; + krb5_principal principal; + krb5_data *string; + krb5_int32 psalttype; + krb5_int32 asalttype; +{ + krb5_boolean weakness; + krb5_error_code kret; + krb5_keyblock primary; + krb5_keyblock alternate; + + DPRINT(DEBUG_CALLS, key_debug_level, ("* key_pwd_is_weak()\n")); + weakness = 0; + + if (master_encblock.key->etype != ETYPE_NULL) { + memset((char *) &primary, 0, sizeof(primary)); + memset((char *) &alternate, 0, sizeof(alternate)); + + kret = key_string_to_keys(kcontext, + principal, + string, + psalttype, + asalttype, + &primary, + &alternate); + if (!kret) { + if (primary.length && + (primary.length == sizeof(mit_des_cblock)) && + mit_des_is_weak_key(primary.contents)) + weakness = 1; + if (alternate.length && + (alternate.length == sizeof(mit_des_cblock)) && + mit_des_is_weak_key(alternate.contents)) + weakness = 1; + if (primary.contents) { + memset((char *) primary.contents, 0, primary.length); + krb5_xfree(primary.contents); + } + if (alternate.contents) { + memset((char *) alternate.contents, 0, alternate.length); + krb5_xfree(alternate.contents); + } + } + } + DPRINT(DEBUG_CALLS, key_debug_level, + ("X key_pwd_is_weak() = %d\n", weakness)); + return(weakness); +} + diff --git a/src/kadmin/v5server/srv_main.c b/src/kadmin/v5server/srv_main.c new file mode 100644 index 000000000..ff1154501 --- /dev/null +++ b/src/kadmin/v5server/srv_main.c @@ -0,0 +1,320 @@ +/* + * kadmin/v5server/srv_main.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_main.c + * Main function of administrative server which speaks new Kerberos V5 + * administrative protocol. + */ + +#include +#include +#include +#include +#include "k5-int.h" +#include "com_err.h" +#if HAVE_STDARG_H +#include +#else /* HAVE_STDARG_H */ +#include +#endif /* HAVE_STDARG_H */ + +#define KADM_MAX_ERRMSG_SIZE 1024 +#ifndef LOG_AUTH +#define LOG_AUTH 0 +#endif /* LOG_AUTH */ + +static const char *usage_format = "%s: usage is %s [-a aclfile] [-d database] [-e enctype] [-i]\n\t[-k mkeytype] [-l langlist] [-m bool] [-r realm] [-t timeout]\n\t[-D dbg] [-M mkeyname].\n"; +static const char *fval_not_number = "%s: value (%s) specified for -%c is not numeric.\n"; +static const char *extra_params = "%s extra paramters beginning with %s... \n"; +static const char *no_memory_fmt = "%s: cannot allocate %d bytes for %s.\n"; +static const char *begin_op_msg = "%s starting."; +static const char *disp_err_fmt = "dispatch error."; +static const char *happy_exit_fmt = "terminating normally."; +static const char *init_error_fmt = "%s: cannot initialize %s.\n"; +static const char *unh_signal_fmt = "exiting on signal %d."; + +static const char *messages_msg = "messages"; +static const char *proto_msg = "protocol module"; +static const char *net_msg = "network"; +static const char *output_msg = "output"; +static const char *acl_msg = "ACLs"; +static const char *key_msg = "key and database"; +static const char *server_name_msg = "Kerberos V5 administrative server"; + +char *programname = (char *) NULL; +static jmp_buf terminal_jmp; + +static void +usage(prog) + char *prog; +{ + fprintf(stderr, usage_format, prog, prog); +} + +/* + * An unhandled signal just proceeds from the setjmp() in main. + */ +static krb5_sigtype +unhandled_signal(signo) + int signo; +{ + longjmp(terminal_jmp, signo); + /* NOTREACHED */ +} + +static void +kadm_com_err_proc(whoami, code, format, ap) + const char *whoami; + long code; + const char *format; + va_list ap; +{ + char *outbuf; + + outbuf = (char *) malloc(KADM_MAX_ERRMSG_SIZE); + if (outbuf) { + char *cp; + sprintf(outbuf, "%s: ", whoami); + if (code) { + strcat(outbuf, error_message(code)); + strcat(outbuf, " - "); + } + cp = &outbuf[strlen(outbuf)]; + vsprintf(cp, format, ap); +#ifndef DEBUG + syslog(LOG_AUTH|LOG_ERR, outbuf); +#endif /* DEBUG */ + strcat(outbuf, "\n"); + fprintf(stderr, outbuf); + free(outbuf); + } + else { + fprintf(stderr, no_memory_fmt, programname, + KADM_MAX_ERRMSG_SIZE, messages_msg); + } +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + extern char *optarg; + int option; + krb5_error_code error; + + int enc_type = -1; + int key_type = -1; + int manual_entry = 0; + krb5_boolean mime_enabled = 0; + int debug_level = 0; + int timeout = -1; + char *acl_file = (char *) NULL; + char *db_file = (char *) NULL; + char *language_list = (char *) NULL; + char *db_realm = (char *) NULL; + char *master_key_name = (char *) NULL; + + /* Kerberatic contexts */ + krb5_context kcontext; + + const char *errmsg; + int signal_number; + + /* + * usage is: + * kadmind5 [-a aclfile] + * [-d database] + * [-e enctype] + * [-i] + * [-k masterkeytype] + * [-l languagelist] + * [-m yesno] + * [-r realmname] + * [-t timeout] + * [-D debuglevel] + * [-M masterkeyname] + */ + error = 0; + while ((option = getopt(argc, argv, "a:d:e:ik:l:m:r:t:D:M:")) != EOF) { + switch (option) { + case 'a': + acl_file = optarg; + break; + case 'd': + db_file = optarg; + break; + case 'e': + if (sscanf(optarg, "%d", &enc_type) != 1) { + fprintf(stderr, fval_not_number, argv[0], optarg, 'e'); + error++; + } + break; + case 'i': + manual_entry++; + break; + case 'k': + if (sscanf(optarg, "%d", &key_type) != 1) { + fprintf(stderr, fval_not_number, argv[0], optarg, 'k'); + error++; + } + break; + case 'l': + language_list = optarg; + break; + case 'm': + if (sscanf(optarg, "%d", &mime_enabled) != 1) { + fprintf(stderr, fval_not_number, argv[0], optarg, 'm'); + error++; + } + break; + case 'r': + db_realm = optarg; + break; + case 't': + if (sscanf(optarg, "%d", &timeout) != 1) { + fprintf(stderr, fval_not_number, argv[0], optarg, 't'); + error++; + } + break; + case 'D': + if (sscanf(optarg, "%d", &debug_level) != 1) { + fprintf(stderr, fval_not_number, argv[0], optarg, 'D'); + error++; + } + break; + case 'M': + master_key_name = optarg; + break; + default: + error++; + break; + } + } + if (optind - argc > 0) { + fprintf(stderr, extra_params, argv[0], argv[optind+1]); + error++; + } + if (error) { + usage(argv[0]); + return(1); + } + + /* + * We've come this far. Our arguments are good. + */ +#ifndef DEBUG + programname = (char *) strrchr(argv[0], '/'); + if (programname) + programname++; + else + programname = argv[0]; +#else /* DEBUG */ + programname = argv[0]; +#endif /* DEBUG */ + krb5_init_context(&kcontext); + krb5_init_ets(kcontext); + openlog(programname, LOG_AUTH|LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); + (void) set_com_err_hook(kadm_com_err_proc); + + if ((signal_number = setjmp(terminal_jmp)) == 0) { + /* + * Initialize signal handling. + */ + 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); + + /* + * Initialize our modules. + */ + error = key_init(kcontext, debug_level, enc_type, key_type, + master_key_name, manual_entry, db_file, db_realm); + if (!error) { + error = acl_init(kcontext, debug_level, acl_file); + if (!error) { + error = output_init(kcontext, debug_level, + language_list, mime_enabled); + if (!error) { + error = net_init(kcontext, + debug_level); + if (!error) { + error = proto_init(kcontext, debug_level, timeout); + + if (error) + errmsg = proto_msg; + } + else + errmsg = net_msg; + } + else + errmsg = output_msg; + } + else + errmsg = acl_msg; + } + else + errmsg = key_msg; + + if (!error) { + /* + * We've successfully initialized here. + */ +#ifndef DEBUG + syslog(LOG_AUTH|LOG_INFO, begin_op_msg, server_name_msg); +#endif /* DEBUG */ + + /* + * net_dispatch() only returns when we're done for some reason. + */ + error = net_dispatch(kcontext); + + com_err(programname, error, + ((error) ? disp_err_fmt : happy_exit_fmt)); + } + else { + /* Initialization error */ + fprintf(stderr, init_error_fmt, programname, errmsg); + } + } + else { + /* Received an unhandled signal */ + com_err(programname, 0, unh_signal_fmt, signal_number); + } + + /* Now clean up after ourselves */ + proto_finish(kcontext, debug_level); + net_finish(kcontext, debug_level); + output_finish(kcontext, debug_level); + acl_finish(kcontext, debug_level); + key_finish(kcontext, debug_level); + krb5_xfree(kcontext); + return(error); +} diff --git a/src/kadmin/v5server/srv_net.c b/src/kadmin/v5server/srv_net.c new file mode 100644 index 000000000..bdef499e0 --- /dev/null +++ b/src/kadmin/v5server/srv_net.c @@ -0,0 +1,664 @@ +/* + * 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 +#include +#include +#include +#include +#ifdef USE_PTHREADS +#include +#endif /* USE_PTHREADS */ + +#include "k5-int.h" +#include "com_err.h" +#include "kadm5_defs.h" +#include "adm.h" + +#define MAX_BIND_TRIES 5 + +/* + * 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 SOMAXCONN +#endif /* MAX_SLAVES */ +#else /* USE_PTHREADS */ +#define net_slave_type pid_t +#ifndef MAX_SLAVES +#define MAX_SLAVES SOMAXCONN +#endif /* MAX_SLAVES */ +#endif /* USE_PTHREADS */ +#define NET_SLAVE_FULL_SLEEP 15 /* 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 = "child 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_binderr_fmt = "%s: cannot bind to network address (%s).\n"; + +static const char *net_select_fmt = "select failed"; +static const char *net_cl_disp_fmt = "client dispatch failed"; +static const char *net_not_ready_fmt = "select 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; + +static jmp_buf shutdown_jmp; + +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() +{ + 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_REQUESTS) { +#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)); + kret = 0; + goto done; + } + else { + /* child */ + 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 */ + close(listen_sock); + DPRINT(DEBUG_SPROC, net_debug_level, + ("| process %d starting\n", getpid())); + 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; + 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)); + } +#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, debug_level) + krb5_context kcontext; + int debug_level; +{ + krb5_error_code kret; + char *local_realm; + char our_host_name[MAXHOSTNAMELEN]; + struct hostent *our_hostent; + struct servent *our_servent; + int bind_tries; + int bind_sleep; + + net_debug_level = debug_level; + DPRINT(DEBUG_CALLS, net_debug_level, ("* net_init()\n")); + + /* + * Get our realm. + */ + if (kret = krb5_get_default_realm(kcontext, &local_realm)) { + fprintf(stderr, net_def_realm_fmt, programname, error_message(kret)); + goto done; + } + + /* Allocate the slave table */ + net_slave_table = (net_slave_info *) malloc(MAX_SLAVES * + sizeof(net_slave_info)); + /* Make our service name */ + net_service_name = (char *) malloc(strlen(local_realm) + + strlen(KRB5_ADM_SERVICE_NAME) + 2); + if ((net_service_name == (char *) NULL) || + (net_slave_table == (net_slave_info *) NULL)) { + kret = ENOMEM; + krb5_xfree(local_realm); + fprintf(stderr, net_no_mem_fmt, programname); + goto done; + } + (void) sprintf(net_service_name, "%s%s%s", + KRB5_ADM_SERVICE_NAME, "/", local_realm); + krb5_xfree(local_realm); + memset((char *) net_slave_table, 0, 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; + + /* 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 = errno; + 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))); + + /* Get the service entry */ + 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; + net_server_addr_init = 1; + DPRINT(DEBUG_HOST, net_debug_level, + ("- service name (%s) is on port %d\n", our_servent->s_name, + our_servent->s_port)); + + /* 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; + } + + /* Bind socket */ + bind_sleep = 1; + bind_tries = 0; + do { + if (bind(net_listen_socket, + &net_server_addr, + sizeof(net_server_addr)) < 0) { + kret = errno; + if (errno != EADDRINUSE) { + fprintf(stderr, net_binderr_fmt, programname, + error_message(kret)); + goto done; + } + else { + bind_tries++; + DPRINT(DEBUG_HOST, net_debug_level, + ("- bind failed sleeping for %d seconds\n", + bind_sleep)); + sleep(bind_sleep); + bind_sleep = bind_sleep << 1; + } + } + else { + DPRINT(DEBUG_HOST, net_debug_level, + ("- bound socket %d on port\n", net_listen_socket)); + break; + } + } while (bind_tries < MAX_BIND_TRIES); + + 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) + 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) + krb5_context kcontext; +{ + krb5_error_code kret; + fd_set mask, readfds; + int nready; + + 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); + + /* + * SIGTERM (or SIGINT, if debug) shuts us down. + */ + signal(SIGTERM, net_shutdown); +#ifdef DEBUG + signal(SIGINT, net_shutdown); +#endif /* DEBUG */ + +#if !USE_PTHREADS + /* + * SIGCHILD indicates end of child process life. + */ + signal(SIGCHLD, net_reaper); +#endif /* !USE_PTHREADS */ + + /* Receive connections on the socket */ + DLOG(DEBUG_OPERATION, net_debug_level, "listening on socket"); + if (setjmp(shutdown_jmp) == 0) { + if (listen(net_listen_socket, SOMAXCONN) < 0) + kret = errno; + } + else + kret = EINTR; + DLOG(DEBUG_OPERATION, net_debug_level, "listen done"); + + while (kret == 0) { + /* + * Prepare to catch signals. + */ + if (setjmp(shutdown_jmp) == 0) { + readfds = mask; + DLOG(DEBUG_OPERATION, net_debug_level, "doing select"); + if ((nready = select(net_listen_socket+1, + &readfds, + (fd_set *) NULL, + (fd_set *) NULL, + (struct timeval *) NULL)) == 0) { + DLOG(DEBUG_OPERATION, net_debug_level, "nobody ready"); + 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); + DLOG(DEBUG_OPERATION, net_debug_level, + "accept connection"); + while (((conn_sock = accept(net_listen_socket, + (struct sockaddr *) &client_addr, + &addrlen)) < 0) && + (errno == EINTR)); + + if (conn_sock < 0) { + kret = errno; + break; + } + DLOG(DEBUG_OPERATION, net_debug_level, + "accepted connection"); + 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; + } + DLOG(DEBUG_OPERATION, net_debug_level, "dispatch done"); + } + else { + com_err(net_dispatch_msg, 0, net_not_ready_fmt); + kret = EIO; + } + } + else { + DLOG(DEBUG_OPERATION, net_debug_level, + "dispatch interrupted by SIGTERM"); + 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); +} diff --git a/src/kadmin/v5server/srv_output.c b/src/kadmin/v5server/srv_output.c new file mode 100644 index 000000000..b1b72a346 --- /dev/null +++ b/src/kadmin/v5server/srv_output.c @@ -0,0 +1,314 @@ +/* + * kadmin/v5server/srv_output.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_output.c - Handle Kerberos output related functions. + */ +#include "k5-int.h" +#include "com_err.h" +#include "kadm5_defs.h" +#include "adm.h" + +static const char *out_adm_success = "Operation successful."; +static const char *out_adm_cmd_unknown = "Command %s unknown."; +static const char *out_adm_pw_unaccept = "Password unacceptable."; +static const char *out_adm_bad_princ = "Principal unknown."; +static const char *out_adm_pwd_too_short = "Password is too short."; +static const char *out_adm_pwd_weak = "Password generates weak key."; +static const char *out_adm_not_allowed = "You are not allowed to change your password."; +static const char *out_adm_bad_pw = "Password incorrect."; +static const char *out_adm_not_in_tkt = "Not an initial ticket."; +static const char *out_adm_cant_change = "Cannot change password."; +static const char *out_adm_lang_unsupp = "Language %s unsupported."; +static const char *out_adm_bad_args = "Bad argument list format for %s command."; +static const char *out_adm_bad_cmd = "Command %s not supported."; +static const char *out_adm_no_cmd = "No command in message."; +static const char *out_adm_no_err = "Unknown error."; +static int output_debug_level = 0; +static int output_mime_enabled = 0; + +static char * +lang_error_message(lang, kval) + char *lang; + krb5_error_code kval; +{ + char *ret; + char *ermsg; + + ermsg = error_message(kval); + if (lang && output_lang_supported(lang)) { + /* + * Just for demonstration purposes. + */ + ret = (char *) malloc(strlen(ermsg)+strlen(lang)+3+1); + if (ret) + sprintf(ret, "%s - %s", lang, ermsg); + } + else { + ret = (char *) malloc(strlen(ermsg)+1); + if (ret) + strcpy(ret, ermsg); + } + return(ret); +} + +static char * +lang_adm_message(lang, ecode, aux, nargs, alist) + char *lang; + krb5_int32 ecode; + krb5_int32 aux; + krb5_int32 nargs; + krb5_data *alist; +{ + char *ret; + const char *ermsg; + char *erarg; + int alen; + + erarg = (char *) NULL; + switch (ecode) { + case KRB5_ADM_SUCCESS: + ermsg = out_adm_success; break; + case KRB5_ADM_CMD_UNKNOWN: + switch (aux) { + case KRB5_ADM_BAD_ARGS: + ermsg = out_adm_bad_args; + erarg = ((nargs >= 1) ? alist[0].data : (char *) NULL); + break; + case KRB5_ADM_BAD_CMD: + ermsg = out_adm_bad_cmd; + erarg = ((nargs >= 1) ? alist[0].data : (char *) NULL); + break; + case KRB5_ADM_NO_CMD: + ermsg = out_adm_no_cmd; + break; + default: + ermsg = out_adm_bad_args; + erarg = ((nargs >= 1) ? alist[0].data : (char *) NULL); + break; + } + break; + case KRB5_ADM_PW_UNACCEPT: + switch (aux) { + case KRB5_ADM_BAD_PRINC: + ermsg = out_adm_bad_princ; + break; + case KRB5_ADM_PWD_TOO_SHORT: + ermsg = out_adm_pwd_too_short; + break; + case KRB5_ADM_PWD_WEAK: + ermsg = out_adm_pwd_weak; + break; + default: + ermsg = out_adm_pw_unaccept; + break; + } + break; + case KRB5_ADM_BAD_PW: + ermsg = out_adm_bad_pw; break; + case KRB5_ADM_NOT_IN_TKT: + ermsg = out_adm_not_in_tkt; break; + case KRB5_ADM_CANT_CHANGE: + switch (aux) { + case KRB5_ADM_BAD_PRINC: + ermsg = out_adm_bad_princ; + break; + case KRB5_ADM_PWD_TOO_SHORT: + ermsg = out_adm_pwd_too_short; + break; + case KRB5_ADM_PWD_WEAK: + ermsg = out_adm_pwd_weak; + break; + case KRB5_ADM_NOT_ALLOWED: + ermsg = out_adm_not_allowed; + break; + default: + ermsg = out_adm_cant_change; + break; + } + break; + case KRB5_ADM_LANG_NOT_SUPPORTED: + ermsg = out_adm_lang_unsupp; + erarg = ((nargs >= 2) ? alist[1].data : (char *) NULL); + break; + default: + ermsg = out_adm_no_err; break; + } + + alen = strlen(ermsg)+1; + if (erarg) + alen += strlen(erarg); + if (lang && output_lang_supported(lang)) { + alen += strlen(lang)+3; + } + ret = (char *) malloc(alen); + if (lang && output_lang_supported(lang)) { + char *xxx; + + /* + * Just for demonstration purposes. + */ + if (ret) { + sprintf(ret, "%s - ", lang); + xxx = &ret[strlen(ret)]; + sprintf(xxx, ermsg, erarg); + } + } + else { + if (ret) + sprintf(ret, ermsg, erarg); + } + return(ret); +} + +static char * +mimeify_text(msg) + char *msg; +{ + char *ret; + /* + * Just for demonstration purposes. + */ + + if (output_mime_enabled) { + ret = (char *) malloc(strlen(msg)+6+3); + if (ret) + sprintf(ret, "MIME: %s\r\n", msg); + if (!ret) + ret = msg; + } + else + ret = msg; + return(ret); +} + +/* + * output_init() - Initialize output context. + */ +krb5_error_code +output_init(kcontext, debug_level, language_list, mime_enabled) + krb5_context kcontext; + int debug_level; + char *language_list; + krb5_boolean mime_enabled; +{ + krb5_error_code kret; + + kret = 0; + output_debug_level = debug_level; + DPRINT(DEBUG_CALLS, output_debug_level, + ("* output_init(llist=%s, mime=%d)\n", + ((language_list) ? language_list : "(null)"), + mime_enabled)); + output_mime_enabled = mime_enabled; + DPRINT(DEBUG_CALLS, output_debug_level, ("X output_init() = %d\n", kret)); + return(kret); +} + +/* + * output_finish - Terminate output context. + */ +void +output_finish(kcontext, debug_level) + krb5_context kcontext; + int debug_level; +{ + DPRINT(DEBUG_CALLS, output_debug_level, ("* output_finish()\n")); + DPRINT(DEBUG_CALLS, output_debug_level, ("X output_finish()\n")); +} + +/* + * output_lang_supported- Is a language supported? + */ +krb5_boolean +output_lang_supported(lname) + char *lname; +{ + krb5_boolean ret; + DPRINT(DEBUG_CALLS, output_debug_level, + ("* output_lang_supported(lang=%s)\n", + ((lname) ? lname : "(default)"))); + ret = 1; + DPRINT(DEBUG_CALLS, output_debug_level, + ("X output_lang_supported() = %d\n", ret)); + return(ret); +} + +/* + * output_errmsg - Return an error message. + */ +char * +output_krb5_errmsg(lang, mime, kval) + char * lang; + krb5_boolean mime; + krb5_error_code kval; +{ + char *ret; + char *ermsg; + int alen; + + DPRINT(DEBUG_CALLS, output_debug_level, + ("* output_krb5_errmsg(v=%d, lang=%s, mime=%d)\n", + kval, ((lang) ? lang : "(default)", mime))); + ermsg = lang_error_message(lang, kval); + if (mime) { + ret = mimeify_text(ermsg); + if (ret != ermsg) + free(ermsg); + } + else + ret = ermsg; + DPRINT(DEBUG_CALLS, output_debug_level, ("X output_krb5_errmsg()\n")); + return(ret); +} + +/* + * output_adm_error - Output an administrative error message string. + */ +char * +output_adm_error(lang, mime, ecode, aux, nargs, alist) + char *lang; + krb5_boolean mime; + krb5_int32 ecode; + krb5_int32 aux; + krb5_int32 nargs; + krb5_data *alist; +{ + char *ermsg; + char *ret; + + DPRINT(DEBUG_CALLS, output_debug_level, + ("* output_adm_err(lang=%s, mime=%d, code=%d/%d)\n", + ((lang) ? lang : "(default)"), mime, ecode, aux)); + ermsg = lang_adm_message(lang, ecode, aux, nargs, alist); + if (mime) { + ret = mimeify_text(ermsg); + if (ret != ermsg) + free(ermsg); + } + else + ret = ermsg; + DPRINT(DEBUG_CALLS, output_debug_level, ("X output_adm_err()\n")); + return(ret); +}