diff --git a/.gitignore b/.gitignore index c8b62c6..307fdb4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /build-stamp /build *.pyc -*.cf \ No newline at end of file diff --git a/ceo/members.py b/ceo/members.py index 87494c1..a61bd1a 100644 --- a/ceo/members.py +++ b/ceo/members.py @@ -23,11 +23,9 @@ cfg = {} def configure(): """Load Members Configuration""" - string_fields = [ 'username_regex', 'shells_file', 'server_url', - 'users_base', 'groups_base', 'sasl_mech', 'sasl_realm', - 'admin_bind_keytab', 'admin_bind_userid', 'realm', - 'admin_principal', 'admin_keytab', 'expired_account_email', - 'mathsoc_regex', 'mathsoc_dont_count' ] + string_fields = [ 'username_regex', 'shells_file', 'ldap_server_url', + 'ldap_users_base', 'ldap_groups_base', 'ldap_sasl_mech', 'ldap_sasl_realm', + 'expire_hook', 'mathsoc_regex', 'mathsoc_dont_count' ] numeric_fields = [ 'min_password_length' ] # read configuration file @@ -93,8 +91,8 @@ def connect(auth_callback): tries = 0 while ld is None: try: - ld = ldapi.connect_sasl(cfg['server_url'], cfg['sasl_mech'], - cfg['sasl_realm'], password) + ld = ldapi.connect_sasl(cfg['ldap_server_url'], cfg['ldap_sasl_mech'], + cfg['ldap_sasl_realm'], password) except ldap.LOCAL_ERROR, e: tries += 1 if tries > 3: @@ -178,10 +176,10 @@ def get(userid): } """ - return ldapi.lookup(ld, 'uid', userid, cfg['users_base']) + return ldapi.lookup(ld, 'uid', userid, cfg['ldap_users_base']) def uid2dn(uid): - return 'uid=%s,%s' % (ldapi.escape(uid), cfg['users_base']) + return 'uid=%s,%s' % (ldapi.escape(uid), cfg['ldap_users_base']) def list_term(term): @@ -200,7 +198,7 @@ def list_term(term): } """ - members = ldapi.search(ld, cfg['users_base'], + members = ldapi.search(ld, cfg['ldap_users_base'], '(&(objectClass=member)(term=%s))', [ term ]) return dict([(member[0], member[1]) for member in members]) @@ -220,7 +218,7 @@ def list_name(name): ] """ - members = ldapi.search(ld, cfg['users_base'], + members = ldapi.search(ld, cfg['ldap_users_base'], '(&(objectClass=member)(cn~=%s))', [ name ]) return dict([(member[0], member[1]) for member in members]) @@ -262,7 +260,7 @@ def list_all(): ] """ - members = ldapi.search(ld, cfg['users_base'], '(objectClass=member)') + members = ldapi.search(ld, cfg['ldap_users_base'], '(objectClass=member)') return dict([(member[0], member[1]) for member in members]) @@ -278,7 +276,7 @@ def list_positions(): ] """ - members = ld.search_s(cfg['users_base'], ldap.SCOPE_SUBTREE, '(position=*)') + members = ld.search_s(cfg['ldap_users_base'], ldap.SCOPE_SUBTREE, '(position=*)') positions = {} for (_, member) in members: for position in member['position']: @@ -299,7 +297,7 @@ def set_position(position, members): Example: set_position('president', ['dtbartle']) """ - res = ld.search_s(cfg['users_base'], ldap.SCOPE_SUBTREE, + res = ld.search_s(cfg['ldap_users_base'], ldap.SCOPE_SUBTREE, '(&(objectClass=member)(position=%s))' % ldapi.escape(position)) old = set([ member['uid'][0] for (_, member) in res ]) new = set(members) @@ -312,7 +310,7 @@ def set_position(position, members): for action in ['del', 'add']: for userid in mods[action]: - dn = 'uid=%s,%s' % (ldapi.escape(userid), cfg['users_base']) + dn = 'uid=%s,%s' % (ldapi.escape(userid), cfg['ldap_users_base']) entry1 = {'position' : [position]} entry2 = {} #{'position' : []} entry = () @@ -325,8 +323,8 @@ def set_position(position, members): def change_group_member(action, group, userid): - user_dn = 'uid=%s,%s' % (ldapi.escape(userid), cfg['users_base']) - group_dn = 'cn=%s,%s' % (ldapi.escape(group), cfg['groups_base']) + user_dn = 'uid=%s,%s' % (ldapi.escape(userid), cfg['ldap_users_base']) + group_dn = 'cn=%s,%s' % (ldapi.escape(group), cfg['ldap_groups_base']) entry1 = {'uniqueMember' : []} entry2 = {'uniqueMember' : [user_dn]} entry = [] @@ -344,7 +342,7 @@ def change_group_member(action, group, userid): ### Shells ### def get_shell(userid): - member = ldapi.lookup(ld, 'uid', userid, cfg['users_base']) + member = ldapi.lookup(ld, 'uid', userid, cfg['ldap_users_base']) if not member: raise NoSuchMember(userid) if 'loginShell' not in member: @@ -363,7 +361,7 @@ def get_shells(): def set_shell(userid, shell): if not shell in get_shells(): raise InvalidArgument("shell", shell, "is not in %s" % cfg['shells_file']) - ldapi.modify(ld, 'uid', userid, cfg['users_base'], [ (ldap.MOD_REPLACE, 'loginShell', [ shell ]) ]) + ldapi.modify(ld, 'uid', userid, cfg['ldap_users_base'], [ (ldap.MOD_REPLACE, 'loginShell', [ shell ]) ]) @@ -420,7 +418,7 @@ def register(userid, term_list): Example: register(3349, ["w2007", "s2007"]) """ - user_dn = 'uid=%s,%s' % (ldapi.escape(userid), cfg['users_base']) + user_dn = 'uid=%s,%s' % (ldapi.escape(userid), cfg['ldap_users_base']) if type(term_list) in (str, unicode): term_list = [ term_list ] @@ -452,7 +450,7 @@ def register(userid, term_list): def register_nonmember(userid, term_list): """Registers a non-member for one or more terms.""" - user_dn = 'uid=%s,%s' % (ldapi.escape(userid), cfg['users_base']) + user_dn = 'uid=%s,%s' % (ldapi.escape(userid), cfg['ldap_users_base']) if type(term_list) in (str, unicode): term_list = [ term_list ] @@ -511,7 +509,7 @@ def group_members(group): Returns a list of group members """ - group = ldapi.lookup(ld, 'cn', group, cfg['groups_base']) + group = ldapi.lookup(ld, 'cn', group, cfg['ldap_groups_base']) if group and 'uniqueMember' in group: r = re.compile('^uid=([^,]*)') @@ -519,11 +517,11 @@ def group_members(group): return [] def expired_accounts(): - members = ldapi.search(ld, cfg['users_base'], + members = ldapi.search(ld, cfg['ldap_users_base'], '(&(objectClass=member)(!(|(term=%s)(nonMemberTerm=%s))))' % (terms.current(), terms.current())) return dict([(member[0], member[1]) for member in members]) def send_account_expired_email(name, email): - args = [ cfg['expired_account_email'], name, email ] - os.spawnv(os.P_WAIT, cfg['expired_account_email'], args) + args = [ cfg['expire_hook'], name, email ] + os.spawnv(os.P_WAIT, cfg['expire_hook'], args) diff --git a/debian/.gitignore b/debian/.gitignore index c5d55af..ae1abc1 100644 --- a/debian/.gitignore +++ b/debian/.gitignore @@ -1,4 +1,9 @@ /ceo.substvars -/ceo +/ceo-common +/ceo-clients +/ceo-daemon +/ceo-gui /files /*.debhelper +/*.debhelper.log +/*.substvars diff --git a/debian/ceo-clients.manpages b/debian/ceo-clients.manpages new file mode 100644 index 0000000..58109bc --- /dev/null +++ b/debian/ceo-clients.manpages @@ -0,0 +1,2 @@ +docs/addclub.1 +docs/addmember.1 diff --git a/debian/ceo-common.dirs b/debian/ceo-common.dirs new file mode 100644 index 0000000..41eb93b --- /dev/null +++ b/debian/ceo-common.dirs @@ -0,0 +1 @@ +etc/csc diff --git a/debian/ceo-common.install b/debian/ceo-common.install new file mode 100644 index 0000000..5561095 --- /dev/null +++ b/debian/ceo-common.install @@ -0,0 +1 @@ +etc/accounts.cf etc/library.cf etc/ops etc/spam etc/csc diff --git a/debian/dirs b/debian/ceo-daemon.dirs similarity index 50% rename from debian/dirs rename to debian/ceo-daemon.dirs index 281c9b5..46d4416 100644 --- a/debian/dirs +++ b/debian/ceo-daemon.dirs @@ -1,3 +1 @@ -usr/bin -etc/csc etc/ldap/schema diff --git a/debian/ceo-daemon.install b/debian/ceo-daemon.install new file mode 100644 index 0000000..106cad9 --- /dev/null +++ b/debian/ceo-daemon.install @@ -0,0 +1 @@ +etc/csc.schema etc/ldap/schema diff --git a/debian/ceo-gui.manpages b/debian/ceo-gui.manpages new file mode 100644 index 0000000..cc4400f --- /dev/null +++ b/debian/ceo-gui.manpages @@ -0,0 +1 @@ +docs/ceo.1 diff --git a/debian/changelog b/debian/changelog index 32101cb..ac2375d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,8 @@ +ceo (0.4.24) stable testing; urgency=low + + * Bump standards version. + + -- Michael Spang Wed, 29 Jul 2009 07:31:24 -0400 ceo (0.4.23) stable testing; urgency=low * CEO library now only finds books that are signed out as being overdue. diff --git a/debian/control b/debian/control index 1f03683..be2ef37 100644 --- a/debian/control +++ b/debian/control @@ -2,13 +2,32 @@ Source: ceo Section: admin Priority: optional Maintainer: Systems Committee -Build-Depends: debhelper (>= 5.0.0), python-dev (>= 2.4), python-support (>= 0.3), libkrb5-dev, libldap2-dev, libsasl2-dev -Standards-Version: 3.8.0 +Build-Depends: debhelper (>= 5.0.0), python-dev (>= 2.4), python-support (>= 0.3), libkrb5-dev, libldap2-dev, libsasl2-dev, libsctp-dev, libprotobuf-c0-dev, libacl1-dev +Standards-Version: 3.8.2 -Package: ceo -Architecture: any -Depends: python-sqlobject, python-psycopg, python-ldap, python-urwid, ${python:Depends}, ${shlibs:Depends} -Description: Computer Science Club Administrative Utilities +Package: ceo-common +Architecture: all +Description: Computer Science Club Common Files This package contains the CSC Electronic Office - and other Computer Science Club administrative - programs. + common files. + +Package: ceo-gui +Architecture: all +Depends: ceo-clients, python-ldap, python-urwid, python-sqlobject, python-psycopg, ${python:Depends}, ${shlibs:Depends} +Description: Computer Science Club Administrative GUI + This package contains the CSC Electronic Office + graphical user interface. + +Package: ceo-clients +Architecture: any +Depends: ceo-common, ${shlibs:Depends} +Description: Computer Science Club Administrative Clients + This package contains the CSC Electronic Office + client programs. + +Package: ceo-daemon +Architecture: any +Depends: ceo-common, ${shlibs:Depends} +Description: Computer Science Club Administrative Daemon + This package contains the CSC Electronic Office + daemon. diff --git a/debian/docs b/debian/docs deleted file mode 100644 index a188e06..0000000 --- a/debian/docs +++ /dev/null @@ -1 +0,0 @@ -docs/* diff --git a/debian/install b/debian/install deleted file mode 100644 index 31aaae0..0000000 --- a/debian/install +++ /dev/null @@ -1,2 +0,0 @@ -etc/accounts.cf.example etc/kerberos.cf.example etc/ldap.cf.example etc/csc -etc/csc.schema etc/ldap/schema diff --git a/debian/manpages b/debian/manpages deleted file mode 100644 index afc0dc3..0000000 --- a/debian/manpages +++ /dev/null @@ -1 +0,0 @@ -docs/*.[0-9] diff --git a/debian/rules b/debian/rules index 4a7303b..fbd2693 100755 --- a/debian/rules +++ b/debian/rules @@ -1,7 +1,7 @@ #!/usr/bin/make -f CFLAGS := -g -O2 -fstack-protector-all -fPIE -LDFLAGS := -pie +LDFLAGS := -pie -Wl,--as-needed build: python setup.py -q build @@ -18,13 +18,14 @@ install: build dh_testdir dh_testroot dh_installdirs - python setup.py -q install --no-compile -O0 --root=debian/ceo - $(MAKE) -C src DESTDIR=$(CURDIR)/debian/ceo PREFIX=/usr install - + python setup.py -q install --no-compile -O0 --root=debian/ceo-gui + $(MAKE) -C src DESTDIR=$(CURDIR)/debian/ceo-clients PREFIX=/usr install_clients + $(MAKE) -C src DESTDIR=$(CURDIR)/debian/ceo-daemon PREFIX=/usr install_daemon + binary-arch: build install dh_testdir dh_testroot - dh_installchangelogs + dh_installchangelogs dh_installdocs dh_installexamples dh_install diff --git a/etc/accounts.cf b/etc/accounts.cf new file mode 100644 index 0000000..8f7f46c --- /dev/null +++ b/etc/accounts.cf @@ -0,0 +1,52 @@ +# /etc/csc/accounts.cf: CSC Accounts Configuration + +### Member Account Options ### + +member_min_id = 20001 +member_max_id = 29999 +member_shell = "/bin/bash" +member_home = "/users" +member_home_acl = "u::rwx,g::rx,o::rx" +member_home_skel = "/users/skel" + +### Club Account Options ### + +club_min_id = 30001 +club_max_id = 39999 +club_shell = "/bin/bash" +club_home = "/users" +club_home_acl = "A+group:%s:rwpRAxaWdDcCs:fd:allow" +club_home_skel = "/users/skel" + +### Administrative Account Options ### + +admin_min_id = 10001 +admin_max_id = 19999 + +### LDAP Options ### + +ldap_server_url = "ldaps://ldap-master.csclub.uwaterloo.ca" +ldap_users_base = "ou=People,dc=csclub,dc=uwaterloo,dc=ca" +ldap_groups_base = "ou=Group,dc=csclub,dc=uwaterloo,dc=ca" +ldap_sudo_base = "ou=SUDOers,dc=csclub,dc=uwaterloo,dc=ca" +ldap_sasl_mech = "GSSAPI" +ldap_sasl_realm = "CSCLUB.UWATERLOO.CA" +ldap_admin_principal = "ceod/admin@CSCLUB.UWATERLOO.CA" + +### Kerberos Options ### + +krb5_realm = "CSCLUB.UWATERLOO.CA" +krb5_admin_principal = "ceod/admin@CSCLUB.UWATERLOO.CA" + +### Spam ### + +notify_hook = "/etc/csc/spam/new-member" +expire_hook = "/etc/csc/spam/expired-account" + +### Miscellaneous ### + +username_regex = "^[a-z][-a-z0-9]*$" +min_password_length = 4 +shells_file = "/etc/shells" +mathsoc_regex = ".*(mat/|vpa/se|computer science|math).*" +mathsoc_dont_count = "cpdohert dlgawley dtbartle mbiggs saforres tmyklebu mgregson rridge dbelange" diff --git a/etc/accounts.cf.example b/etc/accounts.cf.example deleted file mode 100644 index a9b9819..0000000 --- a/etc/accounts.cf.example +++ /dev/null @@ -1,46 +0,0 @@ -# /etc/csc/accounts.cf: CSC Accounts Configuration - -include /etc/csc/ldap.cf -include /etc/csc/kerberos.cf - -### Member Account Options ### - -member_min_id = 20001 -member_max_id = 29999 -member_shell = "/bin/bash" -member_home = "/users" -#member_home_acl = "u::rwx,g::rx,o::rx" -#member_home_dacl = -member_home_acl = - -### Club Account Options ### - -club_min_id = 30001 -club_max_id = 39999 -club_shell = "/bin/bash" -club_home = "/users" -club_home_acl = "A+group:%s:rwpRAxaWdDcCs:fd:allow" - -### Administrative Account Options - -admin_min_id = 10001 -admin_max_id = 19999 -admin_shell = "/bin/bash" -admin_home = "/users" - -### Home Directory Options ### - -skeleton_dir = "/users/skel" -homedir_mode = "0755" -refquota = "4G" - -### Validation Tuning ### - -username_regex = "^[a-z][-a-z0-9]*$" -min_password_length = 4 -shells_file = "/etc/shells" -privileged_group = "staff" -notify_hook = "/etc/csc/notify-hook" -expired_account_email = "/etc/csc/expired-account" -mathsoc_regex = ".*(mat/|vpa/se|computer science|math).*" -mathsoc_dont_count = "cpdohert dlgawley dtbartle mbiggs saforres tmyklebu" diff --git a/etc/kerberos.cf.example b/etc/kerberos.cf.example deleted file mode 100644 index 19ad876..0000000 --- a/etc/kerberos.cf.example +++ /dev/null @@ -1,5 +0,0 @@ -# /etc/csc/kerberos.cf: CSC Kerberos Administration Configuration - -realm = "CSCLUB.UWATERLOO.CA" -admin_principal = "ceo/admin@CSCLUB.UWATERLOO.CA" -admin_keytab = "/etc/csc/ceo.keytab" diff --git a/etc/ldap.cf.example b/etc/ldap.cf.example deleted file mode 100644 index 29755da..0000000 --- a/etc/ldap.cf.example +++ /dev/null @@ -1,14 +0,0 @@ -# /etc/csc/ldap.cf: CSC LDAP Configuration - -server_url = "ldaps:///" - -users_base = "ou=People,dc=csclub,dc=uwaterloo,dc=ca" -groups_base = "ou=Group,dc=csclub,dc=uwaterloo,dc=ca" -sudo_base = "ou=SUDOers,dc=csclub,dc=uwaterloo,dc=ca" - -admin_bind_dn = -admin_bind_keytab = "/etc/csc/ceo.keytab" -admin_bind_userid = "ceo" - -sasl_mech = "GSSAPI" -sasl_realm = "CSCLUB.UWATERLOO.CA" diff --git a/etc/library.cf b/etc/library.cf new file mode 100644 index 0000000..41d11d8 --- /dev/null +++ b/etc/library.cf @@ -0,0 +1,4 @@ +# /etc/csc/library.cf: Library Config + +library_connect_string = "postgres://librarian:PWPWPWPWPWPWPWPWPWPW@127.0.0.1/library" +aws_account_key = "KEYKEYKEYKEYKEYKEYKY" diff --git a/etc/library.cf.example b/etc/library.cf.example deleted file mode 100644 index 030a0f1..0000000 --- a/etc/library.cf.example +++ /dev/null @@ -1,5 +0,0 @@ -# /etc/csc/library.cf: Library Config - -library_db_path = /users/office/library.db -library_connect_string = "sqlite:///home/mgregson/csc/pyceo/test.db" -aws_account_key = "1TNCT5S0RNDV13CJJCG2" \ No newline at end of file diff --git a/etc/ops/adduser b/etc/ops/adduser new file mode 100644 index 0000000..71ee80b --- /dev/null +++ b/etc/ops/adduser @@ -0,0 +1 @@ +ginseng adduser 0x01 diff --git a/etc/spam/expired-account b/etc/spam/expired-account new file mode 100755 index 0000000..a3884ea --- /dev/null +++ b/etc/spam/expired-account @@ -0,0 +1,49 @@ +#!/bin/sh + +name=$1 +email=$2 +shift 2 + +tmp="$(tempfile)" +trap "rm $tmp" 0 +exec >"$tmp" + +echo "From: Computer Science Club " +echo "Reply-to: CSClub Exec " +echo "To: $name <$email>" +echo "Subject: [CSClub] Account Expiration" +echo "" +echo "Hello, + +We noticed that your Computer Science Club membership has expired. We would +like to remind you of the many benefits of being a member of the club: + +* 4 GiB of disk quota +* Web space +* Email address +* Shell account +* Access to our library + +If you would like to renew your membership (the fee is \$2 per term), we have +various methods of doing so: + +* Come by our office (MC 3036) +* Send us a PayPal donation and send us the transaction id; see + http://csclub.uwaterloo.ca/about/donations for details +* Mail us a cheque; here's our address: + Computer Science Club + Math & Computer 3036/3037 + University of Waterloo + 200 University Avenue West + Waterloo, ON N3L 3G1 + Canada + +If you have any questions, feel free to contact us by phone at +(519) 888-4567 x33870, or by email at exec@csclub.uwaterloo.ca. + +Regards, + +The Computer Science Club" + +exec >&- 2>&- +/usr/sbin/sendmail -t -f "ceo@csclub.uwaterloo.ca" < "$tmp" diff --git a/etc/spam/new-member b/etc/spam/new-member new file mode 100755 index 0000000..a6851b3 --- /dev/null +++ b/etc/spam/new-member @@ -0,0 +1,71 @@ +#!/bin/bash -p + +# This is a privileged script. +IFS=$' \t\n' +PATH=/usr/bin:/bin +unset ENV BASH_ENV CDPATH +umask 077 + +prog=$1 +auth=$2 +shift 2 + +tmp="$(tempfile)" +trap "rm $tmp" 0 +exec >"$tmp" + +authrn="$(getent passwd "$auth" | awk -F: '{ print $5 }' | sed -e 's/,.*//')" + +h_from="$prog " +h_to="Membership and Accounts " +h_cc="$authrn <$auth@csclub.uwaterloo.ca>" + +if test "$prog" = addmember; then + user=$1 name=$2 dept=$3 status=$4; shift 4 + subj="New Member: $user" + test -z "$dept" && dept="things unknown" + body="Name: $name +Account: $user +Program: $dept +Added by: $auth" + +elif test "$prog" = addclub; then + user=$1 name=$2 status=$4; shift 4 + subj="New Club Account: $user" + body="Club: $name +Account: $user +Added by: $auth" + +else + exit 1 +fi + +output=$(cat) + +if test "$status" = "failure"; then + subj="$subj (FAILURES)" +fi + +echo "From: $h_from" +echo "To: $h_to" +echo "Cc: $h_cc" +echo "X-Auth-User: $auth" +echo "X-New-User: $user" +echo "X-New-Name: $name" +echo "Subject: $subj" +echo +echo "$body" | fmt -s +echo + +if test "$status" = "success"; then + echo all failures went undetected +elif test -n "$output"; then + echo "$output" +fi + +echo +echo Your Friend, +echo "$prog" + +exec >&2 +env - /usr/sbin/sendmail -t -f "ceo@csclub.uwaterloo.ca" < "$tmp" diff --git a/include/k5-platform.h b/include/k5-platform.h deleted file mode 100644 index affdde1..0000000 --- a/include/k5-platform.h +++ /dev/null @@ -1 +0,0 @@ -/* This file left intentionally blank. */ diff --git a/include/kadm5/admin.h b/include/kadm5/admin.h deleted file mode 100644 index 99d18d4..0000000 --- a/include/kadm5/admin.h +++ /dev/null @@ -1,747 +0,0 @@ -/* - * lib/kadm5/admin.h - * - * Copyright 2001 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. Furthermore if you modify this software you must label - * your software as modified software and not distribute it in such a - * fashion that it might be confused with the original M.I.T. software. - * 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. - * - */ -/* - * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved - * - * $Header$ - */ - -#ifndef __KADM5_ADMIN_H__ -#define __KADM5_ADMIN_H__ - -#if !defined(USE_KADM5_API_VERSION) -#define USE_KADM5_API_VERSION 2 -#endif - -#include -#include -#include -#include -#include -#include -#include - -#define KADM5_ADMIN_SERVICE "kadmin/admin" -#define KADM5_CHANGEPW_SERVICE "kadmin/changepw" -#define KADM5_HIST_PRINCIPAL "kadmin/history" - -typedef krb5_principal kadm5_princ_t; -typedef char *kadm5_policy_t; -typedef long kadm5_ret_t; - -#define KADM5_PW_FIRST_PROMPT \ - (error_message(CHPASS_UTIL_NEW_PASSWORD_PROMPT)) -#define KADM5_PW_SECOND_PROMPT \ - (error_message(CHPASS_UTIL_NEW_PASSWORD_AGAIN_PROMPT)) - -/* - * Successful return code - */ -#define KADM5_OK 0 - -/* - * Field masks - */ - -/* kadm5_principal_ent_t */ -#define KADM5_PRINCIPAL 0x000001 -#define KADM5_PRINC_EXPIRE_TIME 0x000002 -#define KADM5_PW_EXPIRATION 0x000004 -#define KADM5_LAST_PWD_CHANGE 0x000008 -#define KADM5_ATTRIBUTES 0x000010 -#define KADM5_MAX_LIFE 0x000020 -#define KADM5_MOD_TIME 0x000040 -#define KADM5_MOD_NAME 0x000080 -#define KADM5_KVNO 0x000100 -#define KADM5_MKVNO 0x000200 -#define KADM5_AUX_ATTRIBUTES 0x000400 -#define KADM5_POLICY 0x000800 -#define KADM5_POLICY_CLR 0x001000 -/* version 2 masks */ -#define KADM5_MAX_RLIFE 0x002000 -#define KADM5_LAST_SUCCESS 0x004000 -#define KADM5_LAST_FAILED 0x008000 -#define KADM5_FAIL_AUTH_COUNT 0x010000 -#define KADM5_KEY_DATA 0x020000 -#define KADM5_TL_DATA 0x040000 -#ifdef notyet /* Novell */ -#define KADM5_CPW_FUNCTION 0x080000 -#define KADM5_RANDKEY_USED 0x100000 -#endif -#define KADM5_LOAD 0x200000 - -/* all but KEY_DATA and TL_DATA */ -#define KADM5_PRINCIPAL_NORMAL_MASK 0x01ffff - - -/* kadm5_policy_ent_t */ -#define KADM5_PW_MAX_LIFE 0x004000 -#define KADM5_PW_MIN_LIFE 0x008000 -#define KADM5_PW_MIN_LENGTH 0x010000 -#define KADM5_PW_MIN_CLASSES 0x020000 -#define KADM5_PW_HISTORY_NUM 0x040000 -#define KADM5_REF_COUNT 0x080000 - -/* kadm5_config_params */ -#define KADM5_CONFIG_REALM 0x000001 -#define KADM5_CONFIG_DBNAME 0x000002 -#define KADM5_CONFIG_MKEY_NAME 0x000004 -#define KADM5_CONFIG_MAX_LIFE 0x000008 -#define KADM5_CONFIG_MAX_RLIFE 0x000010 -#define KADM5_CONFIG_EXPIRATION 0x000020 -#define KADM5_CONFIG_FLAGS 0x000040 -#define KADM5_CONFIG_ADMIN_KEYTAB 0x000080 -#define KADM5_CONFIG_STASH_FILE 0x000100 -#define KADM5_CONFIG_ENCTYPE 0x000200 -#define KADM5_CONFIG_ADBNAME 0x000400 -#define KADM5_CONFIG_ADB_LOCKFILE 0x000800 -/*#define KADM5_CONFIG_PROFILE 0x001000*/ -#define KADM5_CONFIG_ACL_FILE 0x002000 -#define KADM5_CONFIG_KADMIND_PORT 0x004000 -#define KADM5_CONFIG_ENCTYPES 0x008000 -#define KADM5_CONFIG_ADMIN_SERVER 0x010000 -#define KADM5_CONFIG_DICT_FILE 0x020000 -#define KADM5_CONFIG_MKEY_FROM_KBD 0x040000 -#define KADM5_CONFIG_KPASSWD_PORT 0x080000 -#define KADM5_CONFIG_OLD_AUTH_GSSAPI 0x100000 -#define KADM5_CONFIG_NO_AUTH 0x200000 -#define KADM5_CONFIG_AUTH_NOFALLBACK 0x400000 -#ifdef notyet /* Novell */ -#define KADM5_CONFIG_KPASSWD_SERVER 0x800000 -#endif -/* - * permission bits - */ -#define KADM5_PRIV_GET 0x01 -#define KADM5_PRIV_ADD 0x02 -#define KADM5_PRIV_MODIFY 0x04 -#define KADM5_PRIV_DELETE 0x08 - -/* - * API versioning constants - */ -#define KADM5_MASK_BITS 0xffffff00 - -#define KADM5_STRUCT_VERSION_MASK 0x12345600 -#define KADM5_STRUCT_VERSION_1 (KADM5_STRUCT_VERSION_MASK|0x01) -#define KADM5_STRUCT_VERSION KADM5_STRUCT_VERSION_1 - -#define KADM5_API_VERSION_MASK 0x12345700 -#define KADM5_API_VERSION_1 (KADM5_API_VERSION_MASK|0x01) -#define KADM5_API_VERSION_2 (KADM5_API_VERSION_MASK|0x02) - -typedef struct _kadm5_principal_ent_t_v2 { - krb5_principal principal; - krb5_timestamp princ_expire_time; - krb5_timestamp last_pwd_change; - krb5_timestamp pw_expiration; - krb5_deltat max_life; - krb5_principal mod_name; - krb5_timestamp mod_date; - krb5_flags attributes; - krb5_kvno kvno; - krb5_kvno mkvno; - char *policy; - long aux_attributes; - - /* version 2 fields */ - krb5_deltat max_renewable_life; - krb5_timestamp last_success; - krb5_timestamp last_failed; - krb5_kvno fail_auth_count; - krb5_int16 n_key_data; - krb5_int16 n_tl_data; - krb5_tl_data *tl_data; - krb5_key_data *key_data; -} kadm5_principal_ent_rec_v2, *kadm5_principal_ent_t_v2; - -typedef struct _kadm5_principal_ent_t_v1 { - krb5_principal principal; - krb5_timestamp princ_expire_time; - krb5_timestamp last_pwd_change; - krb5_timestamp pw_expiration; - krb5_deltat max_life; - krb5_principal mod_name; - krb5_timestamp mod_date; - krb5_flags attributes; - krb5_kvno kvno; - krb5_kvno mkvno; - char *policy; - long aux_attributes; -} kadm5_principal_ent_rec_v1, *kadm5_principal_ent_t_v1; - -#if USE_KADM5_API_VERSION == 1 -typedef struct _kadm5_principal_ent_t_v1 - kadm5_principal_ent_rec, *kadm5_principal_ent_t; -#else -typedef struct _kadm5_principal_ent_t_v2 - kadm5_principal_ent_rec, *kadm5_principal_ent_t; -#endif - -typedef struct _kadm5_policy_ent_t { - char *policy; - long pw_min_life; - long pw_max_life; - long pw_min_length; - long pw_min_classes; - long pw_history_num; - long policy_refcnt; -} kadm5_policy_ent_rec, *kadm5_policy_ent_t; - -/* - * Data structure returned by kadm5_get_config_params() - */ -typedef struct _kadm5_config_params { - long mask; - char * realm; - int kadmind_port; - int kpasswd_port; - - char * admin_server; -#ifdef notyet /* Novell */ /* ABI change? */ - char * kpasswd_server; -#endif - - char * dbname; - char * admin_dbname; - char * admin_lockfile; - char * admin_keytab; - char * acl_file; - char * dict_file; - - int mkey_from_kbd; - char * stash_file; - char * mkey_name; - krb5_enctype enctype; - krb5_deltat max_life; - krb5_deltat max_rlife; - krb5_timestamp expiration; - krb5_flags flags; - krb5_key_salt_tuple *keysalts; - krb5_int32 num_keysalts; -} kadm5_config_params; - -/*********************************************************************** - * This is the old krb5_realm_read_params, which I mutated into - * kadm5_get_config_params but which old code (kdb5_* and krb5kdc) - * still uses. - ***********************************************************************/ - -/* - * Data structure returned by krb5_read_realm_params() - */ -typedef struct __krb5_realm_params { - char * realm_profile; - char * realm_dbname; - char * realm_mkey_name; - char * realm_stash_file; - char * realm_kdc_ports; - char * realm_kdc_tcp_ports; - char * realm_acl_file; - krb5_int32 realm_kadmind_port; - krb5_enctype realm_enctype; - krb5_deltat realm_max_life; - krb5_deltat realm_max_rlife; - krb5_timestamp realm_expiration; - krb5_flags realm_flags; - krb5_key_salt_tuple *realm_keysalts; - unsigned int realm_reject_bad_transit:1; - unsigned int realm_kadmind_port_valid:1; - unsigned int realm_enctype_valid:1; - unsigned int realm_max_life_valid:1; - unsigned int realm_max_rlife_valid:1; - unsigned int realm_expiration_valid:1; - unsigned int realm_flags_valid:1; - unsigned int realm_reject_bad_transit_valid:1; - krb5_int32 realm_num_keysalts; -} krb5_realm_params; - -/* - * functions - */ - -#if USE_KADM5_API_VERSION > 1 -krb5_error_code kadm5_get_config_params(krb5_context context, - int use_kdc_config, - kadm5_config_params *params_in, - kadm5_config_params *params_out); - -krb5_error_code kadm5_free_config_params(krb5_context context, - kadm5_config_params *params); - -krb5_error_code kadm5_free_realm_params(krb5_context kcontext, - kadm5_config_params *params); - -krb5_error_code kadm5_get_admin_service_name(krb5_context, char *, - char *, size_t); -#endif - -kadm5_ret_t kadm5_init(char *client_name, char *pass, - char *service_name, -#if USE_KADM5_API_VERSION == 1 - char *realm, -#else - kadm5_config_params *params, -#endif - krb5_ui_4 struct_version, - krb5_ui_4 api_version, - char **db_args, - void **server_handle); -kadm5_ret_t kadm5_init_with_password(char *client_name, - char *pass, - char *service_name, -#if USE_KADM5_API_VERSION == 1 - char *realm, -#else - kadm5_config_params *params, -#endif - krb5_ui_4 struct_version, - krb5_ui_4 api_version, - char **db_args, - void **server_handle); -kadm5_ret_t kadm5_init_with_skey(char *client_name, - char *keytab, - char *service_name, -#if USE_KADM5_API_VERSION == 1 - char *realm, -#else - kadm5_config_params *params, -#endif - krb5_ui_4 struct_version, - krb5_ui_4 api_version, - char **db_args, - void **server_handle); -#if USE_KADM5_API_VERSION > 1 -kadm5_ret_t kadm5_init_with_creds(char *client_name, - krb5_ccache cc, - char *service_name, - kadm5_config_params *params, - krb5_ui_4 struct_version, - krb5_ui_4 api_version, - char **db_args, - void **server_handle); -#endif -kadm5_ret_t kadm5_lock(void *server_handle); -kadm5_ret_t kadm5_unlock(void *server_handle); -kadm5_ret_t kadm5_flush(void *server_handle); -kadm5_ret_t kadm5_destroy(void *server_handle); -kadm5_ret_t kadm5_create_principal(void *server_handle, - kadm5_principal_ent_t ent, - long mask, char *pass); -kadm5_ret_t kadm5_create_principal_3(void *server_handle, - kadm5_principal_ent_t ent, - long mask, - int n_ks_tuple, - krb5_key_salt_tuple *ks_tuple, - char *pass); -kadm5_ret_t kadm5_delete_principal(void *server_handle, - krb5_principal principal); -kadm5_ret_t kadm5_modify_principal(void *server_handle, - kadm5_principal_ent_t ent, - long mask); -kadm5_ret_t kadm5_rename_principal(void *server_handle, - krb5_principal,krb5_principal); -#if USE_KADM5_API_VERSION == 1 -kadm5_ret_t kadm5_get_principal(void *server_handle, - krb5_principal principal, - kadm5_principal_ent_t *ent); -#else -kadm5_ret_t kadm5_get_principal(void *server_handle, - krb5_principal principal, - kadm5_principal_ent_t ent, - long mask); -#endif -kadm5_ret_t kadm5_chpass_principal(void *server_handle, - krb5_principal principal, - char *pass); -kadm5_ret_t kadm5_chpass_principal_3(void *server_handle, - krb5_principal principal, - krb5_boolean keepold, - int n_ks_tuple, - krb5_key_salt_tuple *ks_tuple, - char *pass); -#if USE_KADM5_API_VERSION == 1 -kadm5_ret_t kadm5_randkey_principal(void *server_handle, - krb5_principal principal, - krb5_keyblock **keyblock); -#else -kadm5_ret_t kadm5_randkey_principal(void *server_handle, - krb5_principal principal, - krb5_keyblock **keyblocks, - int *n_keys); -kadm5_ret_t kadm5_randkey_principal_3(void *server_handle, - krb5_principal principal, - krb5_boolean keepold, - int n_ks_tuple, - krb5_key_salt_tuple *ks_tuple, - krb5_keyblock **keyblocks, - int *n_keys); -#endif -kadm5_ret_t kadm5_setv4key_principal(void *server_handle, - krb5_principal principal, - krb5_keyblock *keyblock); - -kadm5_ret_t kadm5_setkey_principal(void *server_handle, - krb5_principal principal, - krb5_keyblock *keyblocks, - int n_keys); - -kadm5_ret_t kadm5_setkey_principal_3(void *server_handle, - krb5_principal principal, - krb5_boolean keepold, - int n_ks_tuple, - krb5_key_salt_tuple *ks_tuple, - krb5_keyblock *keyblocks, - int n_keys); - -kadm5_ret_t kadm5_decrypt_key(void *server_handle, - kadm5_principal_ent_t entry, krb5_int32 - ktype, krb5_int32 stype, krb5_int32 - kvno, krb5_keyblock *keyblock, - krb5_keysalt *keysalt, int *kvnop); - -kadm5_ret_t kadm5_create_policy(void *server_handle, - kadm5_policy_ent_t ent, - long mask); -/* - * kadm5_create_policy_internal is not part of the supported, - * exposed API. It is available only in the server library, and you - * shouldn't use it unless you know why it's there and how it's - * different from kadm5_create_policy. - */ -kadm5_ret_t kadm5_create_policy_internal(void *server_handle, - kadm5_policy_ent_t - entry, long mask); -kadm5_ret_t kadm5_delete_policy(void *server_handle, - kadm5_policy_t policy); -kadm5_ret_t kadm5_modify_policy(void *server_handle, - kadm5_policy_ent_t ent, - long mask); -/* - * kadm5_modify_policy_internal is not part of the supported, - * exposed API. It is available only in the server library, and you - * shouldn't use it unless you know why it's there and how it's - * different from kadm5_modify_policy. - */ -kadm5_ret_t kadm5_modify_policy_internal(void *server_handle, - kadm5_policy_ent_t - entry, long mask); -#if USE_KADM5_API_VERSION == 1 -kadm5_ret_t kadm5_get_policy(void *server_handle, - kadm5_policy_t policy, - kadm5_policy_ent_t *ent); -#else -kadm5_ret_t kadm5_get_policy(void *server_handle, - kadm5_policy_t policy, - kadm5_policy_ent_t ent); -#endif -kadm5_ret_t kadm5_get_privs(void *server_handle, - long *privs); - -kadm5_ret_t kadm5_chpass_principal_util(void *server_handle, - krb5_principal princ, - char *new_pw, - char **ret_pw, - char *msg_ret, - unsigned int msg_len); - -kadm5_ret_t kadm5_free_principal_ent(void *server_handle, - kadm5_principal_ent_t - ent); -kadm5_ret_t kadm5_free_policy_ent(void *server_handle, - kadm5_policy_ent_t ent); - -kadm5_ret_t kadm5_get_principals(void *server_handle, - char *exp, char ***princs, - int *count); - -kadm5_ret_t kadm5_get_policies(void *server_handle, - char *exp, char ***pols, - int *count); - -#if USE_KADM5_API_VERSION > 1 -kadm5_ret_t kadm5_free_key_data(void *server_handle, - krb5_int16 *n_key_data, - krb5_key_data *key_data); -#endif - -kadm5_ret_t kadm5_free_name_list(void *server_handle, char **names, - int count); - -krb5_error_code kadm5_init_krb5_context (krb5_context *); - -#if USE_KADM5_API_VERSION == 1 -/* - * OVSEC_KADM_API_VERSION_1 should be, if possible, compile-time - * compatible with KADM5_API_VERSION_2. Basically, this means we have - * to continue to provide all the old ovsec_kadm function and symbol - * names. - */ - -#define OVSEC_KADM_ACLFILE "/krb5/ovsec_adm.acl" -#define OVSEC_KADM_WORDFILE "/krb5/ovsec_adm.dict" - -#define OVSEC_KADM_ADMIN_SERVICE "ovsec_adm/admin" -#define OVSEC_KADM_CHANGEPW_SERVICE "ovsec_adm/changepw" -#define OVSEC_KADM_HIST_PRINCIPAL "ovsec_adm/history" - -typedef krb5_principal ovsec_kadm_princ_t; -typedef krb5_keyblock ovsec_kadm_keyblock; -typedef char *ovsec_kadm_policy_t; -typedef long ovsec_kadm_ret_t; - -enum ovsec_kadm_salttype { OVSEC_KADM_SALT_V4, OVSEC_KADM_SALT_NORMAL }; -enum ovsec_kadm_saltmod { OVSEC_KADM_MOD_KEEP, OVSEC_KADM_MOD_V4, OVSEC_KADM_MOD_NORMAL }; - -#define OVSEC_KADM_PW_FIRST_PROMPT \ - ((char *) error_message(CHPASS_UTIL_NEW_PASSWORD_PROMPT)) -#define OVSEC_KADM_PW_SECOND_PROMPT \ - ((char *) error_message(CHPASS_UTIL_NEW_PASSWORD_AGAIN_PROMPT)) - -/* - * Successful return code - */ -#define OVSEC_KADM_OK 0 - -/* - * Create/Modify masks - */ -/* principal */ -#define OVSEC_KADM_PRINCIPAL 0x000001 -#define OVSEC_KADM_PRINC_EXPIRE_TIME 0x000002 -#define OVSEC_KADM_PW_EXPIRATION 0x000004 -#define OVSEC_KADM_LAST_PWD_CHANGE 0x000008 -#define OVSEC_KADM_ATTRIBUTES 0x000010 -#define OVSEC_KADM_MAX_LIFE 0x000020 -#define OVSEC_KADM_MOD_TIME 0x000040 -#define OVSEC_KADM_MOD_NAME 0x000080 -#define OVSEC_KADM_KVNO 0x000100 -#define OVSEC_KADM_MKVNO 0x000200 -#define OVSEC_KADM_AUX_ATTRIBUTES 0x000400 -#define OVSEC_KADM_POLICY 0x000800 -#define OVSEC_KADM_POLICY_CLR 0x001000 -/* policy */ -#define OVSEC_KADM_PW_MAX_LIFE 0x004000 -#define OVSEC_KADM_PW_MIN_LIFE 0x008000 -#define OVSEC_KADM_PW_MIN_LENGTH 0x010000 -#define OVSEC_KADM_PW_MIN_CLASSES 0x020000 -#define OVSEC_KADM_PW_HISTORY_NUM 0x040000 -#define OVSEC_KADM_REF_COUNT 0x080000 - -/* - * permission bits - */ -#define OVSEC_KADM_PRIV_GET 0x01 -#define OVSEC_KADM_PRIV_ADD 0x02 -#define OVSEC_KADM_PRIV_MODIFY 0x04 -#define OVSEC_KADM_PRIV_DELETE 0x08 - -/* - * API versioning constants - */ -#define OVSEC_KADM_MASK_BITS 0xffffff00 - -#define OVSEC_KADM_STRUCT_VERSION_MASK 0x12345600 -#define OVSEC_KADM_STRUCT_VERSION_1 (OVSEC_KADM_STRUCT_VERSION_MASK|0x01) -#define OVSEC_KADM_STRUCT_VERSION OVSEC_KADM_STRUCT_VERSION_1 - -#define OVSEC_KADM_API_VERSION_MASK 0x12345700 -#define OVSEC_KADM_API_VERSION_1 (OVSEC_KADM_API_VERSION_MASK|0x01) - - -typedef struct _ovsec_kadm_principal_ent_t { - krb5_principal principal; - krb5_timestamp princ_expire_time; - krb5_timestamp last_pwd_change; - krb5_timestamp pw_expiration; - krb5_deltat max_life; - krb5_principal mod_name; - krb5_timestamp mod_date; - krb5_flags attributes; - krb5_kvno kvno; - krb5_kvno mkvno; - char *policy; - long aux_attributes; -} ovsec_kadm_principal_ent_rec, *ovsec_kadm_principal_ent_t; - -typedef struct _ovsec_kadm_policy_ent_t { - char *policy; - long pw_min_life; - long pw_max_life; - long pw_min_length; - long pw_min_classes; - long pw_history_num; - long policy_refcnt; -} ovsec_kadm_policy_ent_rec, *ovsec_kadm_policy_ent_t; - -/* - * functions - */ -ovsec_kadm_ret_t ovsec_kadm_init(char *client_name, char *pass, - char *service_name, char *realm, - krb5_ui_4 struct_version, - krb5_ui_4 api_version, - char **db_args, - void **server_handle); -ovsec_kadm_ret_t ovsec_kadm_init_with_password(char *client_name, - char *pass, - char *service_name, - char *realm, - krb5_ui_4 struct_version, - krb5_ui_4 api_version, - char ** db_args, - void **server_handle); -ovsec_kadm_ret_t ovsec_kadm_init_with_skey(char *client_name, - char *keytab, - char *service_name, - char *realm, - krb5_ui_4 struct_version, - krb5_ui_4 api_version, - char **db_args, - void **server_handle); -ovsec_kadm_ret_t ovsec_kadm_flush(void *server_handle); -ovsec_kadm_ret_t ovsec_kadm_destroy(void *server_handle); -ovsec_kadm_ret_t ovsec_kadm_create_principal(void *server_handle, - ovsec_kadm_principal_ent_t ent, - long mask, char *pass); -ovsec_kadm_ret_t ovsec_kadm_delete_principal(void *server_handle, - krb5_principal principal); -ovsec_kadm_ret_t ovsec_kadm_modify_principal(void *server_handle, - ovsec_kadm_principal_ent_t ent, - long mask); -ovsec_kadm_ret_t ovsec_kadm_rename_principal(void *server_handle, - krb5_principal,krb5_principal); -ovsec_kadm_ret_t ovsec_kadm_get_principal(void *server_handle, - krb5_principal principal, - ovsec_kadm_principal_ent_t *ent); -ovsec_kadm_ret_t ovsec_kadm_chpass_principal(void *server_handle, - krb5_principal principal, - char *pass); -ovsec_kadm_ret_t ovsec_kadm_randkey_principal(void *server_handle, - krb5_principal principal, - krb5_keyblock **keyblock); -ovsec_kadm_ret_t ovsec_kadm_create_policy(void *server_handle, - ovsec_kadm_policy_ent_t ent, - long mask); -/* - * ovsec_kadm_create_policy_internal is not part of the supported, - * exposed API. It is available only in the server library, and you - * shouldn't use it unless you know why it's there and how it's - * different from ovsec_kadm_create_policy. - */ -ovsec_kadm_ret_t ovsec_kadm_create_policy_internal(void *server_handle, - ovsec_kadm_policy_ent_t - entry, long mask); -ovsec_kadm_ret_t ovsec_kadm_delete_policy(void *server_handle, - ovsec_kadm_policy_t policy); -ovsec_kadm_ret_t ovsec_kadm_modify_policy(void *server_handle, - ovsec_kadm_policy_ent_t ent, - long mask); -/* - * ovsec_kadm_modify_policy_internal is not part of the supported, - * exposed API. It is available only in the server library, and you - * shouldn't use it unless you know why it's there and how it's - * different from ovsec_kadm_modify_policy. - */ -ovsec_kadm_ret_t ovsec_kadm_modify_policy_internal(void *server_handle, - ovsec_kadm_policy_ent_t - entry, long mask); -ovsec_kadm_ret_t ovsec_kadm_get_policy(void *server_handle, - ovsec_kadm_policy_t policy, - ovsec_kadm_policy_ent_t *ent); -ovsec_kadm_ret_t ovsec_kadm_get_privs(void *server_handle, - long *privs); - -ovsec_kadm_ret_t ovsec_kadm_chpass_principal_util(void *server_handle, - krb5_principal princ, - char *new_pw, - char **ret_pw, - char *msg_ret); - -ovsec_kadm_ret_t ovsec_kadm_free_principal_ent(void *server_handle, - ovsec_kadm_principal_ent_t - ent); -ovsec_kadm_ret_t ovsec_kadm_free_policy_ent(void *server_handle, - ovsec_kadm_policy_ent_t ent); - -ovsec_kadm_ret_t ovsec_kadm_free_name_list(void *server_handle, - char **names, int count); - -ovsec_kadm_ret_t ovsec_kadm_get_principals(void *server_handle, - char *exp, char ***princs, - int *count); - -ovsec_kadm_ret_t ovsec_kadm_get_policies(void *server_handle, - char *exp, char ***pols, - int *count); - -#define OVSEC_KADM_FAILURE KADM5_FAILURE -#define OVSEC_KADM_AUTH_GET KADM5_AUTH_GET -#define OVSEC_KADM_AUTH_ADD KADM5_AUTH_ADD -#define OVSEC_KADM_AUTH_MODIFY KADM5_AUTH_MODIFY -#define OVSEC_KADM_AUTH_DELETE KADM5_AUTH_DELETE -#define OVSEC_KADM_AUTH_INSUFFICIENT KADM5_AUTH_INSUFFICIENT -#define OVSEC_KADM_BAD_DB KADM5_BAD_DB -#define OVSEC_KADM_DUP KADM5_DUP -#define OVSEC_KADM_RPC_ERROR KADM5_RPC_ERROR -#define OVSEC_KADM_NO_SRV KADM5_NO_SRV -#define OVSEC_KADM_BAD_HIST_KEY KADM5_BAD_HIST_KEY -#define OVSEC_KADM_NOT_INIT KADM5_NOT_INIT -#define OVSEC_KADM_UNK_PRINC KADM5_UNK_PRINC -#define OVSEC_KADM_UNK_POLICY KADM5_UNK_POLICY -#define OVSEC_KADM_BAD_MASK KADM5_BAD_MASK -#define OVSEC_KADM_BAD_CLASS KADM5_BAD_CLASS -#define OVSEC_KADM_BAD_LENGTH KADM5_BAD_LENGTH -#define OVSEC_KADM_BAD_POLICY KADM5_BAD_POLICY -#define OVSEC_KADM_BAD_PRINCIPAL KADM5_BAD_PRINCIPAL -#define OVSEC_KADM_BAD_AUX_ATTR KADM5_BAD_AUX_ATTR -#define OVSEC_KADM_BAD_HISTORY KADM5_BAD_HISTORY -#define OVSEC_KADM_BAD_MIN_PASS_LIFE KADM5_BAD_MIN_PASS_LIFE -#define OVSEC_KADM_PASS_Q_TOOSHORT KADM5_PASS_Q_TOOSHORT -#define OVSEC_KADM_PASS_Q_CLASS KADM5_PASS_Q_CLASS -#define OVSEC_KADM_PASS_Q_DICT KADM5_PASS_Q_DICT -#define OVSEC_KADM_PASS_REUSE KADM5_PASS_REUSE -#define OVSEC_KADM_PASS_TOOSOON KADM5_PASS_TOOSOON -#define OVSEC_KADM_POLICY_REF KADM5_POLICY_REF -#define OVSEC_KADM_INIT KADM5_INIT -#define OVSEC_KADM_BAD_PASSWORD KADM5_BAD_PASSWORD -#define OVSEC_KADM_PROTECT_PRINCIPAL KADM5_PROTECT_PRINCIPAL -#define OVSEC_KADM_BAD_SERVER_HANDLE KADM5_BAD_SERVER_HANDLE -#define OVSEC_KADM_BAD_STRUCT_VERSION KADM5_BAD_STRUCT_VERSION -#define OVSEC_KADM_OLD_STRUCT_VERSION KADM5_OLD_STRUCT_VERSION -#define OVSEC_KADM_NEW_STRUCT_VERSION KADM5_NEW_STRUCT_VERSION -#define OVSEC_KADM_BAD_API_VERSION KADM5_BAD_API_VERSION -#define OVSEC_KADM_OLD_LIB_API_VERSION KADM5_OLD_LIB_API_VERSION -#define OVSEC_KADM_OLD_SERVER_API_VERSION KADM5_OLD_SERVER_API_VERSION -#define OVSEC_KADM_NEW_LIB_API_VERSION KADM5_NEW_LIB_API_VERSION -#define OVSEC_KADM_NEW_SERVER_API_VERSION KADM5_NEW_SERVER_API_VERSION -#define OVSEC_KADM_SECURE_PRINC_MISSING KADM5_SECURE_PRINC_MISSING -#define OVSEC_KADM_NO_RENAME_SALT KADM5_NO_RENAME_SALT - -#endif /* USE_KADM5_API_VERSION == 1 */ - -#endif /* __KADM5_ADMIN_H__ */ diff --git a/include/kadm5/chpass_util_strings.h b/include/kadm5/chpass_util_strings.h deleted file mode 100644 index affdde1..0000000 --- a/include/kadm5/chpass_util_strings.h +++ /dev/null @@ -1 +0,0 @@ -/* This file left intentionally blank. */ diff --git a/include/kadm5/kadm_err.h b/include/kadm5/kadm_err.h deleted file mode 100644 index 54aa52d..0000000 --- a/include/kadm5/kadm_err.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * ettmp11037.h: - * This file is automatically generated; please do not edit it. - */ - -#include - -#define KADM5_FAILURE (43787520L) -#define KADM5_AUTH_GET (43787521L) -#define KADM5_AUTH_ADD (43787522L) -#define KADM5_AUTH_MODIFY (43787523L) -#define KADM5_AUTH_DELETE (43787524L) -#define KADM5_AUTH_INSUFFICIENT (43787525L) -#define KADM5_BAD_DB (43787526L) -#define KADM5_DUP (43787527L) -#define KADM5_RPC_ERROR (43787528L) -#define KADM5_NO_SRV (43787529L) -#define KADM5_BAD_HIST_KEY (43787530L) -#define KADM5_NOT_INIT (43787531L) -#define KADM5_UNK_PRINC (43787532L) -#define KADM5_UNK_POLICY (43787533L) -#define KADM5_BAD_MASK (43787534L) -#define KADM5_BAD_CLASS (43787535L) -#define KADM5_BAD_LENGTH (43787536L) -#define KADM5_BAD_POLICY (43787537L) -#define KADM5_BAD_PRINCIPAL (43787538L) -#define KADM5_BAD_AUX_ATTR (43787539L) -#define KADM5_BAD_HISTORY (43787540L) -#define KADM5_BAD_MIN_PASS_LIFE (43787541L) -#define KADM5_PASS_Q_TOOSHORT (43787542L) -#define KADM5_PASS_Q_CLASS (43787543L) -#define KADM5_PASS_Q_DICT (43787544L) -#define KADM5_PASS_REUSE (43787545L) -#define KADM5_PASS_TOOSOON (43787546L) -#define KADM5_POLICY_REF (43787547L) -#define KADM5_INIT (43787548L) -#define KADM5_BAD_PASSWORD (43787549L) -#define KADM5_PROTECT_PRINCIPAL (43787550L) -#define KADM5_BAD_SERVER_HANDLE (43787551L) -#define KADM5_BAD_STRUCT_VERSION (43787552L) -#define KADM5_OLD_STRUCT_VERSION (43787553L) -#define KADM5_NEW_STRUCT_VERSION (43787554L) -#define KADM5_BAD_API_VERSION (43787555L) -#define KADM5_OLD_LIB_API_VERSION (43787556L) -#define KADM5_OLD_SERVER_API_VERSION (43787557L) -#define KADM5_NEW_LIB_API_VERSION (43787558L) -#define KADM5_NEW_SERVER_API_VERSION (43787559L) -#define KADM5_SECURE_PRINC_MISSING (43787560L) -#define KADM5_NO_RENAME_SALT (43787561L) -#define KADM5_BAD_CLIENT_PARAMS (43787562L) -#define KADM5_BAD_SERVER_PARAMS (43787563L) -#define KADM5_AUTH_LIST (43787564L) -#define KADM5_AUTH_CHANGEPW (43787565L) -#define KADM5_GSS_ERROR (43787566L) -#define KADM5_BAD_TL_TYPE (43787567L) -#define KADM5_MISSING_CONF_PARAMS (43787568L) -#define KADM5_BAD_SERVER_NAME (43787569L) -#define KADM5_AUTH_SETKEY (43787570L) -#define KADM5_SETKEY_DUP_ENCTYPES (43787571L) -#define KADM5_SETV4KEY_INVAL_ENCTYPE (43787572L) -#define KADM5_SETKEY3_ETYPE_MISMATCH (43787573L) -#define KADM5_MISSING_KRB5_CONF_PARAMS (43787574L) -#define KADM5_XDR_FAILURE (43787575L) -extern const struct error_table et_ovk_error_table; -extern void initialize_ovk_error_table(void); - -/* For compatibility with Heimdal */ -extern void initialize_ovk_error_table_r(struct et_list **list); - -#define ERROR_TABLE_BASE_ovk (43787520L) - -/* for compatibility with older versions... */ -#define init_ovk_err_tbl initialize_ovk_error_table -#define ovk_err_base ERROR_TABLE_BASE_ovk diff --git a/include/kdb.h b/include/kdb.h deleted file mode 100644 index e8a5878..0000000 --- a/include/kdb.h +++ /dev/null @@ -1,500 +0,0 @@ -/* - * include/krb5/kdb.h - * - * Copyright 1990,1991 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. Furthermore if you modify this software you must label - * your software as modified software and not distribute it in such a - * fashion that it might be confused with the original M.I.T. software. - * 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. - * - * - * KDC Database interface definitions. - */ - -/* - * Copyright (C) 1998 by the FundsXpress, INC. - * - * 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 FundsXpress. not be used in advertising or publicity pertaining - * to distribution of the software without specific, written prior - * permission. FundsXpress makes no representations about the suitability of - * this software for any purpose. It is provided "as is" without express - * or implied warranty. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#ifndef KRB5_KDB5__ -#define KRB5_KDB5__ - -/* Salt types */ -#define KRB5_KDB_SALTTYPE_NORMAL 0 -#define KRB5_KDB_SALTTYPE_V4 1 -#define KRB5_KDB_SALTTYPE_NOREALM 2 -#define KRB5_KDB_SALTTYPE_ONLYREALM 3 -#define KRB5_KDB_SALTTYPE_SPECIAL 4 -#define KRB5_KDB_SALTTYPE_AFS3 5 - -/* Attributes */ -#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001 -#define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002 -#define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004 -#define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008 -#define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010 -#define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020 -#define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040 -#define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080 -#define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100 -#define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200 -#define KRB5_KDB_DISALLOW_SVR 0x00001000 -#define KRB5_KDB_PWCHANGE_SERVICE 0x00002000 -#define KRB5_KDB_SUPPORT_DESMD5 0x00004000 -#define KRB5_KDB_NEW_PRINC 0x00008000 - -/* Creation flags */ -#define KRB5_KDB_CREATE_BTREE 0x00000001 -#define KRB5_KDB_CREATE_HASH 0x00000002 - -/* - * Note --- these structures cannot be modified without changing the - * database version number in libkdb.a, but should be expandable by - * adding new tl_data types. - */ -typedef struct _krb5_tl_data { - struct _krb5_tl_data* tl_data_next; /* NOT saved */ - krb5_int16 tl_data_type; - krb5_ui_2 tl_data_length; - krb5_octet * tl_data_contents; -} krb5_tl_data; - -/* - * If this ever changes up the version number and make the arrays be as - * big as necessary. - * - * Currently the first type is the enctype and the second is the salt type. - */ -typedef struct _krb5_key_data { - krb5_int16 key_data_ver; /* Version */ - krb5_int16 key_data_kvno; /* Key Version */ - krb5_int16 key_data_type[2]; /* Array of types */ - krb5_ui_2 key_data_length[2]; /* Array of lengths */ - krb5_octet * key_data_contents[2]; /* Array of pointers */ -} krb5_key_data; - -#define KRB5_KDB_V1_KEY_DATA_ARRAY 2 /* # of array elements */ - -typedef struct _krb5_keysalt { - krb5_int16 type; - krb5_data data; /* Length, data */ -} krb5_keysalt; - -typedef struct _krb5_db_entry_new { - krb5_magic magic; /* NOT saved */ - krb5_ui_2 len; - krb5_ui_4 mask; /* members currently changed/set */ - krb5_flags attributes; - krb5_deltat max_life; - krb5_deltat max_renewable_life; - krb5_timestamp expiration; /* When the client expires */ - krb5_timestamp pw_expiration; /* When its passwd expires */ - krb5_timestamp last_success; /* Last successful passwd */ - krb5_timestamp last_failed; /* Last failed passwd attempt */ - krb5_kvno fail_auth_count; /* # of failed passwd attempt */ - krb5_int16 n_tl_data; - krb5_int16 n_key_data; - krb5_ui_2 e_length; /* Length of extra data */ - krb5_octet * e_data; /* Extra data to be saved */ - - krb5_principal princ; /* Length, data */ - krb5_tl_data * tl_data; /* Linked list */ - krb5_key_data * key_data; /* Array */ -} krb5_db_entry; - -typedef struct _osa_policy_ent_t { - int version; - char *name; - krb5_ui_4 pw_min_life; - krb5_ui_4 pw_max_life; - krb5_ui_4 pw_min_length; - krb5_ui_4 pw_min_classes; - krb5_ui_4 pw_history_num; - krb5_ui_4 policy_refcnt; -} osa_policy_ent_rec, *osa_policy_ent_t; - -typedef void (*osa_adb_iter_policy_func) (void *, osa_policy_ent_t); - -typedef struct __krb5_key_salt_tuple { - krb5_enctype ks_enctype; - krb5_int32 ks_salttype; -} krb5_key_salt_tuple; - -#define KRB5_KDB_MAGIC_NUMBER 0xdbdbdbdb -#define KRB5_KDB_V1_BASE_LENGTH 38 - -#define KRB5_TL_LAST_PWD_CHANGE 0x0001 -#define KRB5_TL_MOD_PRINC 0x0002 -#define KRB5_TL_KADM_DATA 0x0003 -#define KRB5_TL_KADM5_E_DATA 0x0004 -#define KRB5_TL_RB1_CHALLENGE 0x0005 -#ifdef SECURID -#define KRB5_TL_SECURID_STATE 0x0006 -#define KRB5_TL_DB_ARGS 0x7fff -#endif /* SECURID */ -#define KRB5_TL_USER_CERTIFICATE 0x0007 - -/* - * Determines the number of failed KDC requests before DISALLOW_ALL_TIX is set - * on the principal. - */ -#define KRB5_MAX_FAIL_COUNT 5 - -/* XXX depends on knowledge of krb5_parse_name() formats */ -#define KRB5_KDB_M_NAME "K/M" /* Kerberos/Master */ - -/* prompts used by default when reading the KDC password from the keyboard. */ -#define KRB5_KDC_MKEY_1 "Enter KDC database master key" -#define KRB5_KDC_MKEY_2 "Re-enter KDC database master key to verify" - - -extern char *krb5_mkey_pwd_prompt1; -extern char *krb5_mkey_pwd_prompt2; - -/* - * These macros specify the encoding of data within the database. - * - * Data encoding is little-endian. - */ -#include "k5-platform.h" -#define krb5_kdb_decode_int16(cp, i16) \ - *((krb5_int16 *) &(i16)) = load_16_le(cp) -#define krb5_kdb_decode_int32(cp, i32) \ - *((krb5_int32 *) &(i32)) = load_32_le(cp) -#define krb5_kdb_encode_int16(i16, cp) store_16_le(i16, cp) -#define krb5_kdb_encode_int32(i32, cp) store_32_le(i32, cp) - -#define KRB5_KDB_OPEN_RW 0 -#define KRB5_KDB_OPEN_RO 1 - -#ifndef KRB5_KDB_SRV_TYPE_KDC -#define KRB5_KDB_SRV_TYPE_KDC 0x0100 -#endif - -#ifndef KRB5_KDB_SRV_TYPE_ADMIN -#define KRB5_KDB_SRV_TYPE_ADMIN 0x0200 -#endif - -#ifndef KRB5_KDB_SRV_TYPE_PASSWD -#define KRB5_KDB_SRV_TYPE_PASSWD 0x0300 -#endif - -#ifndef KRB5_KDB_SRV_TYPE_OTHER -#define KRB5_KDB_SRV_TYPE_OTHER 0x0400 -#endif - -#define KRB5_KDB_OPT_SET_DB_NAME 0 -#define KRB5_KDB_OPT_SET_LOCK_MODE 1 - -#define KRB5_DB_LOCKMODE_SHARED 0x0001 -#define KRB5_DB_LOCKMODE_EXCLUSIVE 0x0002 -#define KRB5_DB_LOCKMODE_DONTBLOCK 0x0004 -#define KRB5_DB_LOCKMODE_PERMANENT 0x0008 - -/* libkdb.spec */ -krb5_error_code krb5_db_open( krb5_context kcontext, char **db_args, int mode ); -krb5_error_code krb5_db_init ( krb5_context kcontext ); -krb5_error_code krb5_db_create ( krb5_context kcontext, char **db_args ); -krb5_error_code krb5_db_inited ( krb5_context kcontext ); -krb5_error_code kdb5_db_create ( krb5_context kcontext, char **db_args ); -krb5_error_code krb5_db_fini ( krb5_context kcontext ); -const char * krb5_db_errcode2string ( krb5_context kcontext, long err_code ); -krb5_error_code krb5_db_destroy ( krb5_context kcontext, char **db_args ); -krb5_error_code krb5_db_promote ( krb5_context kcontext, char **db_args ); -krb5_error_code krb5_db_get_age ( krb5_context kcontext, char *db_name, time_t *t ); -krb5_error_code krb5_db_set_option ( krb5_context kcontext, int option, void *value ); -krb5_error_code krb5_db_lock ( krb5_context kcontext, int lock_mode ); -krb5_error_code krb5_db_unlock ( krb5_context kcontext ); -krb5_error_code krb5_db_get_principal ( krb5_context kcontext, - krb5_const_principal search_for, - krb5_db_entry *entries, - int *nentries, - krb5_boolean *more ); -krb5_error_code krb5_db_free_principal ( krb5_context kcontext, - krb5_db_entry *entry, - int count ); -krb5_error_code krb5_db_put_principal ( krb5_context kcontext, - krb5_db_entry *entries, - int *nentries); -krb5_error_code krb5_db_delete_principal ( krb5_context kcontext, - krb5_principal search_for, - int *nentries ); -krb5_error_code krb5_db_iterate ( krb5_context kcontext, - char *match_entry, - int (*func) (krb5_pointer, krb5_db_entry *), - krb5_pointer func_arg ); -krb5_error_code krb5_supported_realms ( krb5_context kcontext, - char **realms ); -krb5_error_code krb5_free_supported_realms ( krb5_context kcontext, - char **realms ); -krb5_error_code krb5_db_set_master_key_ext ( krb5_context kcontext, - char *pwd, - krb5_keyblock *key ); -krb5_error_code krb5_db_set_mkey ( krb5_context context, - krb5_keyblock *key); -krb5_error_code krb5_db_get_mkey ( krb5_context kcontext, - krb5_keyblock **key ); -krb5_error_code krb5_db_free_master_key ( krb5_context kcontext, - krb5_keyblock *key ); -krb5_error_code krb5_db_store_master_key ( krb5_context kcontext, - char *db_arg, - krb5_principal mname, - krb5_keyblock *key, - char *master_pwd); -krb5_error_code krb5_db_fetch_mkey ( krb5_context context, - krb5_principal mname, - krb5_enctype etype, - krb5_boolean fromkeyboard, - krb5_boolean twice, - char *db_args, - krb5_data *salt, - krb5_keyblock *key); -krb5_error_code krb5_db_verify_master_key ( krb5_context kcontext, - krb5_principal mprinc, - krb5_keyblock *mkey ); -krb5_error_code -krb5_dbe_find_enctype( krb5_context kcontext, - krb5_db_entry *dbentp, - krb5_int32 ktype, - krb5_int32 stype, - krb5_int32 kvno, - krb5_key_data **kdatap); - - -krb5_error_code krb5_dbe_search_enctype ( krb5_context kcontext, - krb5_db_entry *dbentp, - krb5_int32 *start, - krb5_int32 ktype, - krb5_int32 stype, - krb5_int32 kvno, - krb5_key_data **kdatap); - -krb5_error_code -krb5_db_setup_mkey_name ( krb5_context context, - const char *keyname, - const char *realm, - char **fullname, - krb5_principal *principal); - -krb5_error_code -krb5_dbekd_decrypt_key_data( krb5_context context, - const krb5_keyblock * mkey, - const krb5_key_data * key_data, - krb5_keyblock * dbkey, - krb5_keysalt * keysalt); - -krb5_error_code -krb5_dbekd_encrypt_key_data( krb5_context context, - const krb5_keyblock * mkey, - const krb5_keyblock * dbkey, - const krb5_keysalt * keysalt, - int keyver, - krb5_key_data * key_data); - -krb5_error_code -krb5_dbe_lookup_mod_princ_data( krb5_context context, - krb5_db_entry * entry, - krb5_timestamp * mod_time, - krb5_principal * mod_princ); - - -krb5_error_code -krb5_dbe_update_last_pwd_change( krb5_context context, - krb5_db_entry * entry, - krb5_timestamp stamp); - -krb5_error_code -krb5_dbe_lookup_tl_data( krb5_context context, - krb5_db_entry * entry, - krb5_tl_data * ret_tl_data); - -krb5_error_code -krb5_dbe_create_key_data( krb5_context context, - krb5_db_entry * entry); - - -krb5_error_code -krb5_dbe_update_mod_princ_data( krb5_context context, - krb5_db_entry * entry, - krb5_timestamp mod_date, - krb5_const_principal mod_princ); - -krb5_error_code -krb5_dbe_update_last_pwd_change( krb5_context context, - krb5_db_entry * entry, - krb5_timestamp stamp); - -void *krb5_db_alloc( krb5_context kcontext, - void *ptr, - size_t size ); - -void krb5_db_free( krb5_context kcontext, - void *ptr); - - -krb5_error_code -krb5_dbe_lookup_last_pwd_change( krb5_context context, - krb5_db_entry * entry, - krb5_timestamp * stamp); - -krb5_error_code -krb5_dbe_update_tl_data( krb5_context context, - krb5_db_entry * entry, - krb5_tl_data * new_tl_data); - -krb5_error_code -krb5_dbe_cpw( krb5_context kcontext, - krb5_keyblock * master_key, - krb5_key_salt_tuple * ks_tuple, - int ks_tuple_count, - char * passwd, - int new_kvno, - krb5_boolean keepold, - krb5_db_entry * db_entry); - - -krb5_error_code -krb5_dbe_ark( krb5_context context, - krb5_keyblock * master_key, - krb5_key_salt_tuple * ks_tuple, - int ks_tuple_count, - krb5_db_entry * db_entry); - -krb5_error_code -krb5_dbe_crk( krb5_context context, - krb5_keyblock * master_key, - krb5_key_salt_tuple * ks_tuple, - int ks_tuple_count, - krb5_boolean keepold, - krb5_db_entry * db_entry); - -krb5_error_code -krb5_dbe_apw( krb5_context context, - krb5_keyblock * master_key, - krb5_key_salt_tuple * ks_tuple, - int ks_tuple_count, - char * passwd, - krb5_db_entry * db_entry); - -/* default functions. Should not be directly called */ -/* - * Default functions prototype - */ - -krb5_error_code -krb5_dbe_def_search_enctype( krb5_context kcontext, - krb5_db_entry *dbentp, - krb5_int32 *start, - krb5_int32 ktype, - krb5_int32 stype, - krb5_int32 kvno, - krb5_key_data **kdatap); - -krb5_error_code -krb5_def_store_mkey( krb5_context context, - char *keyfile, - krb5_principal mname, - krb5_keyblock *key, - char *master_pwd); - - -krb5_error_code -krb5_db_def_fetch_mkey( krb5_context context, - krb5_principal mname, - krb5_keyblock *key, - int *kvno, - char *db_args); - -krb5_error_code -krb5_def_verify_master_key( krb5_context context, - krb5_principal mprinc, - krb5_keyblock *mkey); - -krb5_error_code kdb_def_set_mkey ( krb5_context kcontext, - char *pwd, - krb5_keyblock *key ); - -krb5_error_code kdb_def_get_mkey ( krb5_context kcontext, - krb5_keyblock **key ); - -krb5_error_code -krb5_dbe_def_cpw( krb5_context context, - krb5_keyblock * master_key, - krb5_key_salt_tuple * ks_tuple, - int ks_tuple_count, - char * passwd, - int new_kvno, - krb5_boolean keepold, - krb5_db_entry * db_entry); - -krb5_error_code -krb5_def_promote_db(krb5_context, char *, char **); - -krb5_error_code -krb5_db_create_policy( krb5_context kcontext, - osa_policy_ent_t policy); - -krb5_error_code -krb5_db_get_policy ( krb5_context kcontext, - char *name, - osa_policy_ent_t *policy, - int *nentries); - -krb5_error_code -krb5_db_put_policy( krb5_context kcontext, - osa_policy_ent_t policy); - -krb5_error_code -krb5_db_iter_policy( krb5_context kcontext, - char *match_entry, - osa_adb_iter_policy_func func, - void *data); - -krb5_error_code -krb5_db_delete_policy( krb5_context kcontext, - char *policy); - -void -krb5_db_free_policy( krb5_context kcontext, - osa_policy_ent_t policy); - -#define KRB5_KDB_DEF_FLAGS 0 - -#endif /* KRB5_KDB5__ */ diff --git a/src/.gitignore b/src/.gitignore index 05746e8..a7b37bf 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -3,5 +3,11 @@ .nfs* /addmember /addclub +/adduser +/op-adduser /zfsaddhomedir /config-test +/ceod +/ceoc +/ceo.pb-c.c +/ceo.pb-c.h diff --git a/src/Makefile b/src/Makefile index 02c0c81..7d32aef 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,53 +1,79 @@ CFLAGS := -g3 -O2 -Wall -Werror -DDEBUG -LDFLAGS := -L/opt/csw/lib -Wl,-R/opt/csw/lib -L/usr/local/lib -Wl,-R/usr/local/lib -INCLUDES := -I../include $(shell krb5-config --cflags) -override LDFLAGS += -std=gnu99 $(INCLUDES) +LDFLAGS := -Wl,--as-needed +INCLUDES := $(shell krb5-config --cflags) override CFLAGS += -std=gnu99 $(INCLUDES) DESTDIR := PREFIX := /usr/local -BIN_PROGS := addmember addclub zfsaddhomedir simpleaddhomedir +BIN_PROGS := addmember addclub ceod +LIB_PROGS := ceoc op-adduser EXT_PROGS := config-test -LIBCEO_OBJECTS := common.o addhomedir.o -LIBCEO_LDFLAGS := -LIBCEO_PROGS := addmember addclub LDAP_OBJECTS := ldap.o -LDAP_LDFLAGS := -lldap -LDAP_PROGS := addmember addclub +LDAP_LIBS := -lldap +LDAP_PROGS := addmember addclub op-adduser KRB5_OBJECTS := krb5.o kadm.o -KRB5_LDFLAGS := $(shell krb5-config --libs krb5 kadm-client) -KRB5_PROGS := addmember addclub +KRB5_LIBS := $(shell krb5-config --libs krb5 kadm-client) +KRB5_PROGS := addmember addclub op-adduser +HOME_OBJECTS := homedir.o +HOME_LIBS := -lacl +HOME_PROGS := op-adduser +NET_OBJECTS := net.o gss.o ops.o +NET_LIBS := -lsctp $(shell krb5-config --libs gssapi) +NET_PROGS := ceod ceoc +PROTO_OBJECTS := ceo.pb-c.o +PROTO_LIBS := -lprotobuf-c +PROTO_PROGS := op-adduser addmember addclub CONFIG_OBJECTS := config.o parser.o -CONFIG_LDFLAGS := -CONFIG_PROGS := $(OLDCEO_PROGS) $(LDAP_PROGS) $(KRB5_PROGS) $(NET_PROGS) +CONFIG_LIBS := +CONFIG_PROGS := $(LDAP_PROGS) $(KRB5_PROGS) $(NET_PROGS) UTIL_OBJECTS := util.o strbuf.o -UTIL_PROGS := config-test zfsaddhomedir simpleaddhomedir $(CONFIG_PROGS) +UTIL_PROGS := config-test $(CONFIG_PROGS) all: $(BIN_PROGS) $(LIB_PROGS) $(EXT_PROGS) clean: - rm -f $(BIN_PROGS) $(EXT_PROGS) *.o + rm -f $(BIN_PROGS) $(LIB_PROGS) $(EXT_PROGS) *.o ceo.pb-c.c ceo.pb-c.h + +op-adduser.o addmember.o addclub.o: ceo.pb-c.h + +ceo.pb-c.c ceo.pb-c.h: ceo.proto + protoc-c --c_out=. ceo.proto + +ceod: dmaster.o dslave.o + $(CC) $(LDFLAGS) $^ $(CFLAGS) $(LDLIBS) -o $@ config-test: config-test.o parser.o config.o: config.h config-vars.h -install: - install -d $(DESTDIR)$(PREFIX)/bin +install_clients: + install -d $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(PREFIX)/lib/ceod install addmember addclub $(DESTDIR)$(PREFIX)/bin + install ceoc $(DESTDIR)$(PREFIX)/lib/ceod -$(LIBCEO_PROGS): LDFLAGS += $(LIBCEO_LDFLAGS) -$(LIBCEO_PROGS): $(LIBCEO_OBJECTS) -$(LDAP_PROGS): LDFLAGS += $(LDAP_LDFLAGS) +install_daemon: + install -d $(DESTDIR)$(PREFIX)/sbin $(DESTDIR)$(PREFIX)/lib/ceod + install ceod $(DESTDIR)$(PREFIX)/sbin + install op-adduser $(DESTDIR)$(PREFIX)/lib/ceod + +install: install_clients install_daemon + +$(NET_PROGS): LDLIBS += $(NET_LIBS) +$(NET_PROGS): $(NET_OBJECTS) +$(LDAP_PROGS): LDLIBS += $(LDAP_LIBS) $(LDAP_PROGS): $(LDAP_OBJECTS) -$(KRB5_PROGS): LDFLAGS += $(KRB5_LDFLAGS) +$(KRB5_PROGS): LDLIBS += $(KRB5_LIBS) $(KRB5_PROGS): $(KRB5_OBJECTS) -$(CONFIG_PROGS): LDFLAGS += $(CONFIG_LDFLAGS) +$(HOME_PROGS): LDLIBS += $(HOME_LIBS) +$(HOME_PROGS): $(HOME_OBJECTS) +$(PROTO_PROGS): LDLIBS += $(PROTO_LIBS) +$(PROTO_PROGS): $(PROTO_OBJECTS) +$(CONFIG_PROGS): LDLIBS += $(CONFIG_LIBS) $(CONFIG_PROGS): $(CONFIG_OBJECTS) -$(UTIL_PROGS): LDFLAGS += $(UTIL_LDFLAGS) +$(UTIL_PROGS): LDLIBS += $(UTIL_LIBS) $(UTIL_PROGS): $(UTIL_OBJECTS) .PHONY: clean all -.SECONDARY: zfsaddhomedir.o addmember.o addclub.o simpleaddhomedir.o +.SECONDARY: ceoc.o addmember.o addclub.o diff --git a/src/addclub.c b/src/addclub.c index d905d48..27b4f94 100644 --- a/src/addclub.c +++ b/src/addclub.c @@ -12,159 +12,81 @@ #include #include "util.h" -#include "common.h" #include "config.h" #include "ldap.h" #include "krb5.h" #include "kadm.h" -#include "addhomedir.h" +#include "ceo.pb-c.h" char *prog = NULL; -char *user = NULL; -int privileged = 0; - -static int force = 0; -static int no_notify = 0; static char *name = NULL; static char *userid = NULL; static struct option opts[] = { - { "force", 0, NULL, 'f' }, - { "no-notify", 0, NULL, 'q' }, { NULL, 0, NULL, '\0' }, }; +const char *default_lib_dir = "/usr/lib/ceod"; +const char *lib_dir; + static void usage() { fprintf(stderr, "Usage: %s userid clubname\n", prog); exit(2); } -int addclub() { - int krb_ok, user_ok, group_ok, sudo_ok, home_ok; - int id; - char homedir[1024]; - char acl_s[1024] = {0}; +int addclub(void) { + struct strbuf preq = STRBUF_INIT; + struct strbuf pret = STRBUF_INIT; + char cpath[1024]; + char *cargv[] = { "ceoc", "adduser", NULL }; - notice("adding uid=%s cn=%s by %s", userid, name, user); + if (snprintf(cpath, sizeof(cpath), "%s/ceoc", lib_dir) >= sizeof(cpath)) + fatal("path too long"); - if (setreuid(0, 0)) - fatalpe("setreuid"); - if (setregid(0, 0)) - fatalpe("setregid"); + Ceo__AddUser req; + ceo__add_user__init(&req); - if (!force && getpwnam(userid) != NULL) - deny("user %s already exists", userid); + req.username = userid; + req.realname = name; + req.type = CEO__ADD_USER__TYPE__CLUB; - snprintf(homedir, sizeof(homedir), "%s/%s", club_home, userid); - ceo_krb5_init(); - ceo_ldap_init(); - ceo_kadm_init(); + strbuf_grow(&preq, ceo__add_user__get_packed_size(&req)); + strbuf_setlen(&preq, ceo__add_user__pack(&req, (uint8_t *)preq.buf)); - if (ceo_user_exists(userid)) - deny("user %s already exists in LDAP", userid); - if (ceo_group_exists(userid)) - deny("group %s already exists in LDAP", userid); + if (spawnvem(cpath, cargv, environ, &preq, &pret, 0)) + return 1; - if ((id = ceo_new_uid(club_min_id, club_max_id)) <= 0) - fatal("no available uids in range [%zd, %zd]", club_min_id, club_max_id); + Ceo__AddUserResponse *ret = ceo__add_user_response__unpack(&protobuf_c_default_allocator, + pret.len, (uint8_t *)pret.buf); + if (!ret) + fatal("failed to unpack response"); - snprintf(acl_s, sizeof(acl_s), club_home_acl, userid); - - krb_ok = ceo_del_princ(userid); - if (!krb_ok) - notice("successfully cleared principal for %s", userid); - - user_ok = krb_ok || ceo_add_user(userid, users_base, "club", name, homedir, - club_shell, id, NULL); - if (!user_ok) - notice("successfully created account for %s", userid); - - group_ok = user_ok || ceo_add_group(userid, groups_base, id); - if (!group_ok) - notice("successfully created group for %s", userid); - - sudo_ok = user_ok || ceo_add_group_sudo(userid, sudo_base); - if (!sudo_ok) - notice("successfully added group sudo entry for %s", userid); - - home_ok = user_ok || ceo_create_home(homedir, refquota, id, id, homedir_mode, acl_s); - if (!home_ok) - notice("successfully created home directory for %s", userid); - - notice("done uid=%s", userid); - - if (!no_notify && !user_ok) { - int pid; - int hkp[2]; - FILE *hkf; - int status; - - if (pipe(hkp)) - errorpe("pipe"); - - fflush(stdout); - fflush(stderr); - - pid = fork(); - - if (!pid) { - fclose(stdout); - fclose(stderr); - close(hkp[1]); - dup2(hkp[0], 0); - exit(execl(notify_hook, notify_hook, prog, user, userid, name, NULL)); - } - - hkf = fdopen(hkp[1], "w"); - - if (group_ok) - fprintf(hkf, "failed to create group\n"); - if (home_ok) - fprintf(hkf, "failed to create home directory\n"); - if (!group_ok && !home_ok) - fprintf(hkf, "all failures went undetected\n"); - - fclose(hkf); - - waitpid(pid, &status, 0); - - if (WIFEXITED(status) && WEXITSTATUS(status)) - notice("hook %s exited with status %d", notify_hook, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - notice("hook %s killed by signal %d", notify_hook, WTERMSIG(status)); + for (int i = 0; i < ret->n_messages; i++) { + if (ret->messages[i]->status) + error("%s", ret->messages[i]->message); + else + notice("%s", ret->messages[i]->message); } - ceo_kadm_cleanup(); - ceo_ldap_cleanup(); - ceo_krb5_cleanup(); + ceo__add_user_response__free_unpacked(ret, &protobuf_c_default_allocator); + strbuf_release(&preq); + strbuf_release(&pret); - return krb_ok || user_ok || group_ok || home_ok; + return 0; } int main(int argc, char *argv[]) { int opt; + int ret; - prog = basename(argv[0]); - init_log(prog, LOG_PID, LOG_AUTHPRIV); + prog = xstrdup(basename(argv[0])); + init_log(prog, 0, LOG_AUTHPRIV); configure(); - user = ceo_get_user(); - privileged = ceo_get_privileged(); - while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { switch (opt) { - case 'f': - if (!privileged) - deny("not privileged enough to force"); - force = 1; - break; - case 'q': - if (!privileged) - deny("not privileged enough to suppress notifications"); - no_notify = 1; - break; case '?': usage(); break; @@ -173,11 +95,18 @@ int main(int argc, char *argv[]) { } } - if (argc - optind != 2) + if (argc - optind != 2 && argc - optind != 3) usage(); userid = argv[optind++]; name = argv[optind++]; - return addclub(); + lib_dir = getenv("CEO_LIB_DIR") ?: default_lib_dir; + + ret = addclub(); + + free_config(); + free(prog); + + return ret; } diff --git a/src/addhomedir.c b/src/addhomedir.c deleted file mode 100644 index aa4e7b4..0000000 --- a/src/addhomedir.c +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "addhomedir.h" -#include "util.h" -#include "config.h" -#include "krb5.h" - -int ceo_create_home(char *homedir, char *refquota, uid_t uid, gid_t gid, char *mode, char *acl) { - char uid_str[16], gid_str[16]; - char *zfs_argv[] = { "ssh", "ceo@ginseng", "/usr/sbin/simpleaddhomedir", \ - homedir, skeleton_dir, uid_str, gid_str, mode, NULL }; - int ret = 0; - - assert(homedir[0]); - snprintf(uid_str, sizeof(uid_str), "%ld", (long)uid); - snprintf(gid_str, sizeof(gid_str), "%ld", (long)uid); - if(!acl[0]) acl = NULL; - - ceo_krb5_auth(admin_bind_userid, admin_bind_keytab); - if(spawnv("/usr/bin/ssh", zfs_argv)) { - errorpe("failed calling simpleaddhomedir for %s", homedir); - ret = -1; - } - ceo_krb5_deauth(); - - return ret; -} diff --git a/src/addhomedir.h b/src/addhomedir.h deleted file mode 100644 index 6e0d091..0000000 --- a/src/addhomedir.h +++ /dev/null @@ -1,3 +0,0 @@ -#include - -int ceo_create_home(char *, char *, uid_t, gid_t, char *, char *); diff --git a/src/addmember.c b/src/addmember.c index bd227f1..138a356 100644 --- a/src/addmember.c +++ b/src/addmember.c @@ -12,19 +12,13 @@ #include #include "util.h" -#include "common.h" #include "config.h" #include "ldap.h" #include "krb5.h" #include "kadm.h" -#include "addhomedir.h" +#include "ceo.pb-c.h" char *prog = NULL; -char *user = NULL; -int privileged = 0; - -static int force = 0; -static int no_notify = 0; static int use_stdin = 0; @@ -34,148 +28,78 @@ static char *program = NULL; static char password[1024]; static struct option opts[] = { - { "force", 0, NULL, 'f' }, - { "no-notify", 0, NULL, 'q' }, { "stdin", 0, NULL, 's' }, { NULL, 0, NULL, '\0' }, }; +const char *default_lib_dir = "/usr/lib/ceod"; +const char *lib_dir; + static void usage() { fprintf(stderr, "Usage: %s userid realname [program]\n", prog); exit(2); } -int addmember() { - int krb_ok, user_ok, group_ok, home_ok; - int id; - char homedir[1024]; - char acl_s[1024] = {0}; +int addmember(void) { + struct strbuf preq = STRBUF_INIT; + struct strbuf pret = STRBUF_INIT; + char cpath[1024]; + char *cargv[] = { "ceoc", "adduser", NULL }; - notice("adding uid=%s cn=%s program=%s by %s", userid, name, program, user); - - if (setreuid(0, 0)) - fatalpe("setreuid"); - if (setregid(0, 0)) - fatalpe("setregid"); - - if (!force && getpwnam(userid) != NULL) - deny("user %s already exists", userid); - - snprintf(homedir, sizeof(homedir), "%s/%s", member_home, userid); + if (snprintf(cpath, sizeof(cpath), "%s/ceoc", lib_dir) >= sizeof(cpath)) + fatal("path too long"); if (ceo_read_password(password, sizeof(password), use_stdin)) return 1; - ceo_krb5_init(); - ceo_ldap_init(); - ceo_kadm_init(); + Ceo__AddUser req; + ceo__add_user__init(&req); - if (ceo_user_exists(userid)) - deny("user %s already exists in LDAP", userid); - if (ceo_group_exists(userid)) - deny("group %s already exists in LDAP", userid); + req.username = userid; + req.password = password; + req.program = program; + req.realname = name; + req.type = CEO__ADD_USER__TYPE__MEMBER; - if ((id = ceo_new_uid(member_min_id, member_max_id)) <= 0) - fatal("no available uids in range [%zd, %zd]", member_min_id, member_max_id); + strbuf_grow(&preq, ceo__add_user__get_packed_size(&req)); + strbuf_setlen(&preq, ceo__add_user__pack(&req, (uint8_t *)preq.buf)); - if (*member_home_acl) { - snprintf(acl_s, sizeof(acl_s), member_home_acl, userid); + if (spawnvem(cpath, cargv, environ, &preq, &pret, 0)) + return 1; + + Ceo__AddUserResponse *ret = ceo__add_user_response__unpack(&protobuf_c_default_allocator, + pret.len, (uint8_t *)pret.buf); + if (!ret) + fatal("failed to unpack response"); + + for (int i = 0; i < ret->n_messages; i++) { + if (ret->messages[i]->status) + error("%s", ret->messages[i]->message); + else + notice("%s", ret->messages[i]->message); } - krb_ok = ceo_del_princ(userid); - krb_ok = krb_ok || ceo_add_princ(userid, password); - if (!krb_ok) - notice("successfully created principal for %s", userid); + ceo__add_user_response__free_unpacked(ret, &protobuf_c_default_allocator); + strbuf_release(&preq); + strbuf_release(&pret); - user_ok = krb_ok || ceo_add_user(userid, users_base, "member", name, homedir, - member_shell, id, "program", program, NULL); - if (!user_ok) - notice("successfully created account for %s", userid); - - group_ok = user_ok || ceo_add_group(userid, groups_base, id); - if (!group_ok) - notice("successfully created group for %s", userid); - - home_ok = user_ok || ceo_create_home(homedir, refquota, id, id, homedir_mode, acl_s); - if (!home_ok) - notice("successfully created home directory for %s", userid); - - notice("done uid=%s", userid); - - if (!no_notify && !user_ok) { - int pid; - int hkp[2]; - FILE *hkf; - int status; - - if (pipe(hkp)) - errorpe("pipe"); - - fflush(stdout); - fflush(stderr); - - pid = fork(); - - if (!pid) { - fclose(stdout); - fclose(stderr); - close(hkp[1]); - dup2(hkp[0], 0); - exit(execl(notify_hook, notify_hook, prog, user, userid, name, program, NULL)); - } - - hkf = fdopen(hkp[1], "w"); - - if (group_ok) - fprintf(hkf, "failed to create group\n"); - if (home_ok) - fprintf(hkf, "failed to create home directory\n"); - if (!group_ok && !home_ok) - fprintf(hkf, "all failures went undetected\n"); - - fclose(hkf); - - waitpid(pid, &status, 0); - - if (WIFEXITED(status) && WEXITSTATUS(status)) - notice("hook %s exited with status %d", notify_hook, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - notice("hook %s killed by signal %d", notify_hook, WTERMSIG(status)); - } - - ceo_kadm_cleanup(); - ceo_ldap_cleanup(); - ceo_krb5_cleanup(); - - return krb_ok || user_ok || group_ok || home_ok; + return 0; } int main(int argc, char *argv[]) { int opt; + int ret; - prog = basename(argv[0]); - init_log(prog, LOG_PID, LOG_AUTHPRIV); + prog = xstrdup(basename(argv[0])); + init_log(prog, 0, LOG_AUTHPRIV); configure(); - user = ceo_get_user(); - privileged = ceo_get_privileged(); - while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { switch (opt) { case 's': use_stdin = 1; break; - case 'f': - if (!privileged) - deny("not privileged enough to force"); - force = 1; - break; - case 'q': - if (!privileged) - deny("not privileged enough to suppress notifications"); - no_notify = 1; - break; case '?': usage(); break; @@ -193,5 +117,12 @@ int main(int argc, char *argv[]) { if (argc - optind) program = argv[optind++]; - return addmember(); + lib_dir = getenv("CEO_LIB_DIR") ?: default_lib_dir; + + ret = addmember(); + + free_config(); + free(prog); + + return ret; } diff --git a/src/ceo.proto b/src/ceo.proto new file mode 100644 index 0000000..386bc77 --- /dev/null +++ b/src/ceo.proto @@ -0,0 +1,23 @@ +package ceo; + +message StatusMessage { + required int32 status = 1; + required string message = 2; +} + +message AddUser { + enum Type { + MEMBER = 1; + CLUB = 2; + } + + required Type type = 1; + required string username = 2; + optional string password = 3; + optional string realname = 4; + optional string program = 5; +} + +message AddUserResponse { + repeated StatusMessage messages = 1; +} diff --git a/src/ceoc.c b/src/ceoc.c new file mode 100644 index 0000000..1eb6d78 --- /dev/null +++ b/src/ceoc.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include + +#include "util.h" +#include "net.h" +#include "gss.h" +#include "ops.h" +#include "config.h" + +char *prog = NULL; + +static struct option opts[] = { + { NULL, 0, NULL, '\0' }, +}; + +static void usage() { + fprintf(stderr, "Usage: %s op\n", prog); + exit(2); +} + +static void send_gss_token(int sock, struct sockaddr *addr, socklen_t addrlen, gss_buffer_t token) { + OM_uint32 maj_stat, min_stat; + + if (sctp_sendmsg(sock, token->value, token->length, + addr, addrlen, MSG_AUTH, 0, 0, 0, 0) < 0) + fatalpe("sctp_sendmsg"); + + maj_stat = gss_release_buffer(&min_stat, token); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_release_buffer", maj_stat, min_stat); +} + +static void client_gss_auth(int sock, struct sockaddr *addr, socklen_t addrlen) { + gss_buffer_desc incoming_tok, outgoing_tok; + struct sctp_meta msg_meta; + struct strbuf msg = STRBUF_INIT; + int complete; + + complete = initial_client_token(&outgoing_tok); + + for (;;) { + if (outgoing_tok.length) + send_gss_token(sock, addr, addrlen, &outgoing_tok); + else if (!complete) + fatal("no token to send during auth"); + + if (complete) + break; + + if (!receive_one_message(sock, &msg_meta, &msg)) + fatal("connection closed during auth"); + + if (msg_meta.sinfo.sinfo_ppid != MSG_AUTH) + fatal("unexpected message type 0x%x", msg_meta.sinfo.sinfo_ppid); + + incoming_tok.value = msg.buf; + incoming_tok.length = msg.len; + + complete = process_client_token(&incoming_tok, &outgoing_tok); + } + + strbuf_release(&msg); +} + +void run_remote(struct op *op, struct strbuf *in, struct strbuf *out) { + const char *hostname = op->hostname; + int sock = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); + struct sockaddr_in addr; + struct sctp_meta response_meta; + + if (!in->len) + fatal("no data to send"); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(9987); + addr.sin_addr = op->addr; + + struct sctp_event_subscribe events; + memset(&events, 0, sizeof(events)); + events.sctp_data_io_event = 1; + events.sctp_association_event = 1; + events.sctp_address_event = 1; + events.sctp_send_failure_event = 1; + events.sctp_peer_error_event = 1; + events.sctp_shutdown_event = 1; + events.sctp_partial_delivery_event = 1; + events.sctp_adaptation_layer_event = 1; + + if (setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events))) + fatalpe("setsockopt"); + + client_acquire_creds("ceod", hostname); + client_gss_auth(sock, (sa *)&addr, sizeof(addr)); + + if (sctp_sendmsg(sock, in->buf, in->len, (struct sockaddr *)&addr, + sizeof(addr), op->id, 0, 0, 0, 0) < 0) + fatalpe("sctp_sendmsg"); + + if (!receive_one_message(sock, &response_meta, out)) + fatal("no response received for op %s", op->name); + + if (response_meta.sinfo.sinfo_ppid != op->id) + fatal("wrong ppid from server: expected %d got %d", op->id, response_meta.sinfo.sinfo_ppid); + + if (sctp_sendmsg(sock, NULL, 0, (struct sockaddr *)&addr, + sizeof(addr), 0, SCTP_EOF, 0, 0 ,0) < 0) + fatalpe("sctp_sendmsg"); +} + +int client_main(char *op_name) { + struct op *op = find_op(op_name); + + if (!op) + fatal("no such op: %s", op_name); + + struct strbuf in = STRBUF_INIT; + struct strbuf out = STRBUF_INIT; + + if (strbuf_read(&in, STDIN_FILENO, 0) < 0) + fatalpe("read"); + + run_remote(op, &in, &out); + + if (strbuf_write(&out, STDOUT_FILENO) < 0) + fatalpe("write"); + + strbuf_release(&in); + strbuf_release(&out); + + return 0; +} + +int main(int argc, char *argv[]) { + int opt; + int ret; + char *op; + + prog = xstrdup(basename(argv[0])); + init_log(prog, 0, LOG_USER); + + configure(); + setup_ops(); + setup_fqdn(); + + while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { + switch (opt) { + case '?': + usage(); + break; + default: + fatal("error parsing arguments"); + } + } + + if (argc - optind != 1) + usage(); + + op = argv[optind++]; + + ret = client_main(op); + + free_gss(); + free_fqdn(); + free_config(); + free_ops(); + free(prog); + + return ret; +} diff --git a/src/common.c b/src/common.c deleted file mode 100644 index a8ce9a7..0000000 --- a/src/common.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include - -#include "common.h" -#include "util.h" -#include "config.h" - -int ceo_get_privileged() { - int uid = getuid(); - - // root is privileged - if (!uid) - return 1; - - if (privileged_group) { - struct group *privgrp = getgrnam(privileged_group); - int pgid; - gid_t grps[128]; - int count, i; - if (!privgrp) - return 0; - pgid = privgrp->gr_gid; - - count = getgroups(sizeof(grps)/sizeof(*grps), grps); - for (i = 0; i < count; i++) - if (grps[i] == pgid) - return 1; - } - - return 0; -} - -char *ceo_get_user() { - struct passwd *pwent = getpwuid(getuid()); - if (pwent == NULL) - fatal("could not determine user"); - return xstrdup(pwent->pw_name); -} - -void ceo_notify_hook(int argc, ...) { - va_list args; - char **argv; - int i = 0; - - va_start(args, argc); - - argv = (char **)xmalloc(sizeof(char *) * (argc + 1)); - - while (i < argc) - argv[i++] = va_arg(args, char *); - - argv[i++] = NULL; - spawnv(notify_hook, argv); - - va_end(args); -} diff --git a/src/common.h b/src/common.h deleted file mode 100644 index cd17722..0000000 --- a/src/common.h +++ /dev/null @@ -1,3 +0,0 @@ -int ceo_get_privileged(); -char *ceo_get_user(); -void ceo_notify_hook(int, ...); diff --git a/src/config-vars.h b/src/config-vars.h index fa6c53b..0380dc4 100644 --- a/src/config-vars.h +++ b/src/config-vars.h @@ -1,33 +1,24 @@ -CONFIG_STR(server_url) - -CONFIG_STR(users_base) -CONFIG_STR(groups_base) -CONFIG_STR(sudo_base) - -CONFIG_STR(skeleton_dir) -CONFIG_STR(homedir_mode) -CONFIG_STR(refquota) - CONFIG_STR(member_shell) CONFIG_INT(member_min_id) CONFIG_INT(member_max_id) CONFIG_STR(member_home) -CONFIG_STR(member_home_acl) +CONFIG_STR(member_home_skel) CONFIG_STR(club_shell) CONFIG_INT(club_min_id) CONFIG_INT(club_max_id) CONFIG_STR(club_home) -CONFIG_STR(club_home_acl) +CONFIG_STR(club_home_skel) CONFIG_STR(notify_hook) -CONFIG_STR(realm) +CONFIG_STR(krb5_realm) +CONFIG_STR(krb5_admin_principal) -CONFIG_STR(admin_principal) -CONFIG_STR(admin_keytab) - -CONFIG_STR(admin_bind_userid) -CONFIG_STR(admin_bind_keytab) - -CONFIG_STR(privileged_group) +CONFIG_STR(ldap_server_url) +CONFIG_STR(ldap_users_base) +CONFIG_STR(ldap_groups_base) +CONFIG_STR(ldap_sudo_base) +CONFIG_STR(ldap_sasl_mech) +CONFIG_STR(ldap_sasl_realm) +CONFIG_STR(ldap_admin_principal) diff --git a/src/config.c b/src/config.c index d08b260..7dffe9e 100644 --- a/src/config.c +++ b/src/config.c @@ -54,7 +54,7 @@ void config_var(char *var, char *val) { } } -void configure() { +void configure(void) { int i; char conffile[1024]; @@ -80,3 +80,12 @@ void configure() { } } } + +void free_config(void) { + for (int i = 0; i < sizeof(config_vars)/sizeof(*config_vars); i++) { + if (config_vars[i].type == CONFIG_TYPE_STR) { + free(*(char **)config_vars[i].p); + *(char **)config_vars[i].p = NULL; + } + } +} diff --git a/src/config.h b/src/config.h index 789843f..57527c5 100644 --- a/src/config.h +++ b/src/config.h @@ -4,6 +4,7 @@ #undef CONFIG_STR #undef CONFIG_INT -void configure(); +void configure(void); +void free_config(void); extern const char *config_dir; diff --git a/src/daemon.h b/src/daemon.h new file mode 100644 index 0000000..12bf804 --- /dev/null +++ b/src/daemon.h @@ -0,0 +1,10 @@ +/* dmain.c */ +extern int terminate; +extern int fatal_signal; + +/* dslave.c */ +void slave_main(int sock, struct sockaddr *addr); +void setup_slave(void); + +/* builtin-adduser.c */ +extern void builtin_adduser(struct sctp_meta *in, struct sctp_meta *out); diff --git a/src/dmaster.c b/src/dmaster.c new file mode 100644 index 0000000..e621ade --- /dev/null +++ b/src/dmaster.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "net.h" +#include "config.h" +#include "gss.h" +#include "daemon.h" +#include "ldap.h" +#include "kadm.h" +#include "krb5.h" +#include "ops.h" + +static struct option opts[] = { + { "detach", 0, NULL, 'd' }, + { NULL, 0, NULL, '\0' }, +}; + +char *prog = NULL; + +int terminate = 0; +int fatal_signal; + +static int detach = 0; + +static void usage() { + fprintf(stderr, "Usage: %s [--detach]\n", prog); + exit(2); +} + +static void signal_handler(int sig) { + if (sig == SIGTERM || sig == SIGINT) { + const char *s = (sig == SIGTERM) ? "terminated" : "interrupt"; + notice("shutting down (%s)", s); + terminate = 1; + fatal_signal = sig; + signal(sig, SIG_DFL); + } else if (sig == SIGSEGV) { + error("segmentation fault"); + signal(sig, SIG_DFL); + raise(sig); + } else if (sig != SIGCHLD) { + fatal("unhandled signal %d", sig); + } +} + +static void setup_signals(void) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = signal_handler; + + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGSEGV, &sa, NULL); + + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, SIG_IGN); +} + +static void setup_daemon(void) { + if (chdir("/")) + fatalpe("chdir('/')"); + if (detach) { + pid_t pid = fork(); + if (pid < 0) + fatalpe("fork"); + if (pid) + exit(0); + if (setsid() < 0) + fatalpe("setsid"); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + } +} + +static void setup_auth(void) { + if (setenv("KRB5CCNAME", "MEMORY:ceod", 1)) + fatalpe("setenv"); + server_acquire_creds("ceod"); +} + +static void accept_one_client(int server) { + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, addrlen); + + int client = accept(server, (sa *)&addr, &addrlen); + if (client < 0) { + if (errno == EINTR) + return; + fatalpe("accept"); + } + + pid_t pid = fork(); + if (!pid) { + close(server); + slave_main(client, (sa *)&addr); + exit(0); + } + + close(client); +} + +static int master_main(void) { + int sock; + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(9987); + addr.sin_addr.s_addr = INADDR_ANY; + + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP); + if (sock < 0) + fatalpe("socket"); + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) + fatalpe("bind"); + + if (listen(sock, 128)) + fatalpe("listen"); + + setup_daemon(); + setup_fqdn(); + setup_signals(); + setup_auth(); + setup_ops(); + + notice("now accepting connections"); + + while (!terminate) + accept_one_client(sock); + + free_gss(); + free_fqdn(); + free_ops(); + + return 0; +} + +int main(int argc, char *argv[]) { + int opt; + int ret; + + prog = xstrdup(basename(argv[0])); + init_log(prog, LOG_PID, LOG_DAEMON); + + configure(); + + while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { + switch (opt) { + case 'd': + detach = 1; + break; + case '?': + usage(); + break; + default: + fatal("error parsing arguments"); + } + } + + if (argc != optind) + usage(); + + ret = master_main(); + + free_config(); + free(prog); + + return ret; +} diff --git a/src/dslave.c b/src/dslave.c new file mode 100644 index 0000000..dd373cf --- /dev/null +++ b/src/dslave.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "strbuf.h" +#include "net.h" +#include "config.h" +#include "gss.h" +#include "daemon.h" +#include "ldap.h" +#include "kadm.h" +#include "krb5.h" +#include "ops.h" + +static void signal_handler(int sig) { + if (sig == SIGSEGV) { + error("segmentation fault"); + signal(sig, SIG_DFL); + raise(sig); + } else if (sig != SIGCHLD) { + fatal("unhandled signal %d", sig); + } +} + +static void setup_slave_sigs(void) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = signal_handler; + + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGSEGV, &sa, NULL); + + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_IGN); + + if (terminate) + raise(fatal_signal); +} + +static void setup_client(int client, struct sockaddr *addr) { + struct sctp_event_subscribe events; + memset(&events, 0, sizeof(events)); + events.sctp_data_io_event = 1; + events.sctp_address_event = 1; + events.sctp_send_failure_event = 1; + events.sctp_peer_error_event = 1; + events.sctp_partial_delivery_event = 1; + events.sctp_adaptation_layer_event = 1; + + if (setsockopt(client, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events))) + fatalpe("setsockopt(SCTP_EVENTS)"); +} + +static void handle_auth_message(struct strbuf *in, struct strbuf *out) { + gss_buffer_desc incoming_tok, outgoing_tok; + OM_uint32 maj_stat, min_stat; + + incoming_tok.value = in->buf; + incoming_tok.length = in->len; + + process_server_token(&incoming_tok, &outgoing_tok); + + strbuf_add(out, outgoing_tok.value, outgoing_tok.length); + + if (outgoing_tok.length) { + maj_stat = gss_release_buffer(&min_stat, &outgoing_tok); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_release_buffer", maj_stat, min_stat); + } +} + +static void handle_op_message(uint32_t in_type, struct strbuf *in, struct strbuf *out) { + struct op *op = get_local_op(in_type); + char *envp[16]; + + debug("running op: %s", op->name); + + if (!op->name) + fatal("operation %x does not exist", in_type); + + /* TEMPORARY */ + if (!client_username()) + fatal("unathenticated"); + + make_env(envp, "LANG", "C", "CEO_USER", client_username(), + "CEO_CONFIG_DIR", config_dir, NULL); + char *argv[] = { op->path, NULL, }; + + if (spawnvem(op->path, argv, envp, in, out, 0)) + fatal("child %s failed", op->path); + + free_env(envp); +} + +static void handle_one_message(int sock, struct sctp_meta *in_meta, struct strbuf *in) { + struct strbuf out = STRBUF_INIT; + uint32_t ppid = in_meta->sinfo.sinfo_ppid; + + if (ppid == MSG_AUTH) + handle_auth_message(in, &out); + else + handle_op_message(ppid, in, &out); + + if (out.len && sctp_sendmsg(sock, out.buf, out.len, + (sa *)&in_meta->from, in_meta->fromlen, ppid, + 0, 0, 0, 0) < 0) + fatalpe("sctp_sendmsg"); + + strbuf_release(&out); +} + +void slave_main(int sock, struct sockaddr *addr) { + char addrstr[INET_ADDRSTRLEN]; + struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; + struct sctp_meta msg_meta; + struct strbuf msg = STRBUF_INIT; + + if (addr->sa_family != AF_INET) + fatal("unsupported address family %d", addr->sa_family); + + if (!inet_ntop(AF_INET, &addr_in->sin_addr, addrstr, sizeof(addrstr))) + fatalpe("inet_ntop"); + + notice("accepted connection from %s", addrstr); + + setup_slave_sigs(); + setup_client(sock, addr); + + while (!terminate) { + if (!receive_one_message(sock, &msg_meta, &msg)) + break; + handle_one_message(sock, &msg_meta, &msg); + } + + notice("connection closed by peer %s", addrstr); + + strbuf_release(&msg); + + /* stuff allocated by dmaster */ + free_gss(); + free_config(); + free_fqdn(); + free_ops(); + free(prog); +} + diff --git a/src/gss.c b/src/gss.c new file mode 100644 index 0000000..13d0c6d --- /dev/null +++ b/src/gss.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include + +#include "util.h" +#include "gss.h" +#include "net.h" +#include "strbuf.h" + +static gss_cred_id_t my_creds = GSS_C_NO_CREDENTIAL; +static gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; +static gss_name_t peer_name = GSS_C_NO_NAME; +static gss_name_t imported_service = GSS_C_NO_NAME; +static char *peer_principal; +static char *peer_username; +static OM_uint32 ret_flags; +static int complete; +char service_name[128]; + +void free_gss(void) { + OM_uint32 maj_stat, min_stat; + + if (peer_name) { + maj_stat = gss_release_name(&min_stat, &peer_name); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_release_name", maj_stat, min_stat); + } + + if (imported_service) { + maj_stat = gss_release_name(&min_stat, &imported_service); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_release_name", maj_stat, min_stat); + } + + if (context_handle) { + maj_stat = gss_delete_sec_context(&min_stat, &context_handle, GSS_C_NO_BUFFER); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_delete_sec_context", maj_stat, min_stat); + } + + if (my_creds) { + maj_stat = gss_release_cred(&min_stat, &my_creds); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_release_creds", maj_stat, min_stat); + } + + free(peer_principal); + free(peer_username); +} + +static void display_status(char *prefix, OM_uint32 code, int type) { + OM_uint32 maj_stat, min_stat; + gss_buffer_desc msg; + OM_uint32 msg_ctx = 0; + + maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID, + &msg_ctx, &msg); + logmsg(LOG_ERR, "%s: %s", prefix, (char *)msg.value); + gss_release_buffer(&min_stat, &msg); + + while (msg_ctx) { + maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID, + &msg_ctx, &msg); + logmsg(LOG_ERR, "additional: %s", (char *)msg.value); + gss_release_buffer(&min_stat, &msg); + } +} + +void gss_fatal(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) { + logmsg(LOG_ERR, "fatal: %s", msg); + display_status("major", maj_stat, GSS_C_GSS_CODE); + display_status("minor", min_stat, GSS_C_MECH_CODE); + exit(1); +} + +static void import_service(const char *service, const char *hostname) { + OM_uint32 maj_stat, min_stat; + gss_buffer_desc buf_desc; + + if (snprintf(service_name, sizeof(service_name), + "%s@%s", service, hostname) >= sizeof(service_name)) + fatal("service name too long"); + + buf_desc.value = service_name; + buf_desc.length = strlen(service_name); + + maj_stat = gss_import_name(&min_stat, &buf_desc, + GSS_C_NT_HOSTBASED_SERVICE, &imported_service); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_import_name", maj_stat, min_stat); +} + +static void check_services(OM_uint32 flags) { + debug("gss services: %sconf %sinteg %smutual %sreplay %ssequence", + flags & GSS_C_CONF_FLAG ? "+" : "-", + flags & GSS_C_INTEG_FLAG ? "+" : "-", + flags & GSS_C_MUTUAL_FLAG ? "+" : "-", + flags & GSS_C_REPLAY_FLAG ? "+" : "-", + flags & GSS_C_SEQUENCE_FLAG ? "+" : "-"); + if (~flags & GSS_C_CONF_FLAG) + fatal("confidentiality service required"); + if (~flags & GSS_C_INTEG_FLAG) + fatal("integrity service required"); + if (~flags & GSS_C_MUTUAL_FLAG) + fatal("mutual authentication required"); +} + +void server_acquire_creds(const char *service) { + OM_uint32 maj_stat, min_stat; + OM_uint32 time_rec; + + if (!strlen(fqdn.buf)) + fatal("empty fqdn"); + + import_service(service, fqdn.buf); + + notice("acquiring credentials for %s", service_name); + + maj_stat = gss_acquire_cred(&min_stat, imported_service, GSS_C_INDEFINITE, + GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &my_creds, + NULL, &time_rec); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_acquire_cred", maj_stat, min_stat); + + if (time_rec != GSS_C_INDEFINITE) + fatal("credentials valid for %d seconds (oops)", time_rec); +} + +void client_acquire_creds(const char *service, const char *hostname) { + import_service(service, hostname); +} + +static char *princ_to_username(char *princ) { + char *ret = xstrdup(princ); + char *c = strchr(ret, '@'); + if (c) + *c = '\0'; + return ret; +} + +int process_server_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok) { + OM_uint32 maj_stat, min_stat; + OM_uint32 time_rec; + gss_OID name_type; + gss_buffer_desc peer_princ; + + if (complete) + fatal("unexpected %zd-byte token from peer", incoming_tok->length); + + maj_stat = gss_accept_sec_context(&min_stat, &context_handle, my_creds, + incoming_tok, GSS_C_NO_CHANNEL_BINDINGS, &peer_name, NULL, + outgoing_tok, &ret_flags, &time_rec, NULL); + if (maj_stat == GSS_S_COMPLETE) { + check_services(ret_flags); + + complete = 1; + + maj_stat = gss_display_name(&min_stat, peer_name, &peer_princ, &name_type); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_display_name", maj_stat, min_stat); + + peer_principal = xstrdup((char *)peer_princ.value); + peer_username = princ_to_username((char *)peer_princ.value); + + notice("client authenticated as %s", peer_principal); + debug("context expires in %d seconds", time_rec); + + maj_stat = gss_release_buffer(&min_stat, &peer_princ); + if (maj_stat != GSS_S_COMPLETE) + gss_fatal("gss_release_buffer", maj_stat, min_stat); + + } else if (maj_stat != GSS_S_CONTINUE_NEEDED) { + gss_fatal("gss_accept_sec_context", maj_stat, min_stat); + } + + return complete; +} + +int process_client_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok) { + OM_uint32 maj_stat, min_stat; + OM_uint32 time_rec; + gss_OID_desc krb5 = *gss_mech_krb5; + + if (complete) + fatal("unexpected token from peer"); + + maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &context_handle, + imported_service, &krb5, GSS_C_MUTUAL_FLAG | + GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, + GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, + incoming_tok, NULL, outgoing_tok, &ret_flags, + &time_rec); + if (maj_stat == GSS_S_COMPLETE) { + notice("server authenticated as %s", service_name); + notice("context expires in %d seconds", time_rec); + + check_services(ret_flags); + + complete = 1; + + } else if (maj_stat != GSS_S_CONTINUE_NEEDED) { + gss_fatal("gss_init_sec_context", maj_stat, min_stat); + } + + return complete; +} + +int initial_client_token(gss_buffer_t outgoing_tok) { + return process_client_token(GSS_C_NO_BUFFER, outgoing_tok); +} + +char *client_principal(void) { + return peer_principal; +} + +char *client_username(void) { + return peer_username; +} + diff --git a/src/gss.h b/src/gss.h new file mode 100644 index 0000000..9a14a61 --- /dev/null +++ b/src/gss.h @@ -0,0 +1,12 @@ +#include +#include + +void server_acquire_creds(const char *service); +void client_acquire_creds(const char *service, const char *hostname); +void gss_fatal(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat); +int process_server_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok); +int process_client_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok); +int initial_client_token(gss_buffer_t outgoing_tok); +char *client_principal(void); +char *client_username(void); +void free_gss(void); diff --git a/src/simpleaddhomedir.c b/src/homedir.c similarity index 66% rename from src/simpleaddhomedir.c rename to src/homedir.c index 2e30867..b808fcd 100644 --- a/src/simpleaddhomedir.c +++ b/src/homedir.c @@ -1,63 +1,43 @@ #include #include -#include -#include -#include #include +#include +#include +#include +#include #include -#include + +#include "homedir.h" #include "util.h" +#include "config.h" -int main(int argc, char *argv[]) { - if(argc < 6) { - fprintf(stderr, "Usage: simpleaddhomedir homedir skeldir uid gid mode\n"); - return 1; - } - - char *homedir = argv[1]; - char *skeldir = argv[2]; - uid_t uid, gid; - char *mkdir_bin = "/bin/mkdir"; - char *chmod_bin = "/bin/chmod"; - char *dataset = homedir; - char *create_argv[] = { "mkdir", dataset, NULL }; - char *mode_argv[] = { "chmod", "0755", homedir, NULL }; - DIR *skel; +int ceo_create_home(char *homedir, char *skel, uid_t uid, gid_t gid) { + int mask; + DIR *skeldir; struct dirent *skelent; - assert(homedir[0]); - uid = atol(argv[3]); - gid = atol(argv[4]); + mask = umask(0); - if (setreuid(0, 0)) - fatalpe("ogawd"); - - if(spawnv(mkdir_bin, create_argv)) - return 1; - //Quotas are ignored now, or so I'm told. - /* if(spawnv(zfs_bin, quota_argv)) */ - /* return 1; */ - if(spawnv(chmod_bin, mode_argv)) - return 1; - //Fuck ACLs. The instructions I got didn't include them. - /* if(acl && spawnv(chmod_bin, acl_argv)) */ - /* return 1; */ - - skel = opendir(skeldir); - if (!skel) { - errorpe("failed to open %s", skeldir); + if (mkdir(homedir, 0755)) { + errorpe("failed to create %s", homedir); return -1; } - while ((skelent = readdir(skel))) { + skeldir = opendir(skel); + if (!skeldir) { + errorpe("failed to open %s", skel); + return -1; + } + + while ((skelent = readdir(skeldir))) { struct stat sb; char src[PATH_MAX], dest[PATH_MAX]; if (!strcmp(skelent->d_name, ".") || !strcmp(skelent->d_name, "..")) continue; - snprintf(src, sizeof(src), "%s/%s", skeldir, skelent->d_name); - snprintf(dest, sizeof(dest), "/%s/%s", homedir, skelent->d_name); + snprintf(src, sizeof(src), "%s/%s", skel, skelent->d_name); + snprintf(dest, sizeof(dest), "%s/%s", homedir, skelent->d_name); lstat(src, &sb); if (sb.st_uid || sb.st_gid) { @@ -95,6 +75,7 @@ int main(int argc, char *argv[]) { break; } } + if (fchown(destfd, uid, gid)) errorpe("chown: %s", dest); @@ -127,12 +108,14 @@ int main(int argc, char *argv[]) { } } - closedir(skel); + closedir(skeldir); if (chown(homedir, uid, gid)) { errorpe("failed to chown %s", homedir); return -1; } + umask(mask); + return 0; } diff --git a/src/homedir.h b/src/homedir.h new file mode 100644 index 0000000..0637867 --- /dev/null +++ b/src/homedir.h @@ -0,0 +1,3 @@ +#include + +int ceo_create_home(char *homedir, char *skel, uid_t uid, gid_t gid); diff --git a/src/kadm.c b/src/kadm.c index fed6888..90790e9 100644 --- a/src/kadm.c +++ b/src/kadm.c @@ -14,35 +14,40 @@ void ceo_kadm_init() { kadm5_config_params params; memset((void *) ¶ms, 0, sizeof(params)); - retval = kadm5_init_with_skey(admin_principal, admin_keytab, + debug("kadmin: initializing using keytab for %s", krb5_admin_principal); + + retval = kadm5_init_with_skey(krb5_admin_principal, NULL, KADM5_ADMIN_SERVICE, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, NULL, &handle); - if (retval) { + if (retval || !handle) { com_err(prog, retval, "while initializing kadm5"); exit(1); } } void ceo_kadm_cleanup() { + debug("kadmin: cleaning up"); kadm5_destroy(handle); } int ceo_add_princ(char *user, char *password) { krb5_error_code retval; - kadm5_principal_ent_rec princ; + krb5_principal princ; memset((void *) &princ, 0, sizeof(princ)); - if ((retval = krb5_parse_name(context, user, &princ.principal))) { + debug("kadmin: adding principal %s", user); + + if ((retval = krb5_parse_name(context, user, &princ))) { com_err(prog, retval, "while parsing principal name"); return retval; } - if ((retval = kadm5_create_principal(handle, &princ, KADM5_PRINCIPAL, password))) { + if ((retval = kadm5_chpass_principal(handle, princ, password))) { com_err(prog, retval, "while creating principal"); return retval; } - krb5_free_principal(context, princ.principal); + krb5_free_principal(context, princ); return 0; } @@ -50,6 +55,8 @@ int ceo_del_princ(char *user) { krb5_error_code retval; krb5_principal princ; + debug("kadmin: deleting principal %s", user); + if ((retval = krb5_parse_name(context, user, &princ))) { com_err(prog, retval, "while parsing principal name"); return retval; diff --git a/src/krb5.c b/src/krb5.c index 31d07e5..d045733 100644 --- a/src/krb5.c +++ b/src/krb5.c @@ -1,4 +1,5 @@ #include + #include #include @@ -34,36 +35,36 @@ void ceo_krb5_init() { set_com_err_hook(com_err_hk); + debug("krb5: initializing context"); + retval = krb5_init_context(&context); if (retval) com_err(prog, retval, "while initializing krb5"); - retval = krb5_set_default_realm(context, realm); + retval = krb5_set_default_realm(context, krb5_realm); if (retval) com_err(prog, retval, "while setting default realm"); } -void ceo_krb5_auth(char *principal, char *ktname) { +void ceo_krb5_auth(char *principal) { krb5_error_code retval; krb5_creds creds; krb5_principal princ; - krb5_keytab keytab; krb5_ccache cache; krb5_get_init_creds_opt options; krb5_get_init_creds_opt_init(&options); memset(&creds, 0, sizeof(creds)); + debug("krb5: getting TGT using keytab for %s", principal); + if ((retval = krb5_parse_name(context, principal, &princ))) - com_err(prog, retval, "while resolving user %s", admin_bind_userid); + com_err(prog, retval, "while resolving user %s", principal); if ((retval = krb5_cc_default(context, &cache))) com_err(prog, retval, "while resolving credentials cache"); - if ((retval = krb5_kt_resolve(context, ktname, &keytab))) - com_err(prog, retval, "while resolving keytab %s", admin_bind_keytab); - - if ((retval = krb5_get_init_creds_keytab(context, &creds, princ, keytab, 0, NULL, &options))) + if ((retval = krb5_get_init_creds_keytab(context, &creds, princ, NULL, 0, NULL, &options))) com_err(prog, retval, "while getting initial credentials"); if ((retval = krb5_cc_initialize(context, cache, princ))) @@ -73,7 +74,6 @@ void ceo_krb5_auth(char *principal, char *ktname) { com_err(prog, retval, "while storing credentials"); krb5_free_cred_contents(context, &creds); - krb5_kt_close(context, keytab); krb5_free_principal(context, princ); krb5_cc_close(context, cache); } @@ -82,6 +82,8 @@ void ceo_krb5_deauth() { krb5_error_code retval; krb5_ccache cache; + debug("krb5: destroying credentials"); + if ((retval = krb5_cc_default(context, &cache))) com_err(prog, retval, "while resolving credentials cache"); @@ -90,6 +92,7 @@ void ceo_krb5_deauth() { } void ceo_krb5_cleanup() { + debug("krb5: cleaning up"); krb5_free_context(context); } diff --git a/src/krb5.h b/src/krb5.h index 9538b32..5877b79 100644 --- a/src/krb5.h +++ b/src/krb5.h @@ -8,7 +8,7 @@ extern krb5_context context; void ceo_krb5_init(); void ceo_krb5_cleanup(); -void ceo_krb5_auth(char *, char *); +void ceo_krb5_auth(char *); void ceo_krb5_deauth(); int ceo_read_password(char *, unsigned int, int); diff --git a/src/ldap.c b/src/ldap.c index dd7d34d..2154036 100644 --- a/src/ldap.c +++ b/src/ldap.c @@ -170,7 +170,7 @@ int ceo_add_group_sudo(char *group, char *basedn) { return ret; } -int ceo_add_user(char *uid, char *basedn, char *objclass, char *cn, char *home, char *shell, int no, ...) { +int ceo_add_user(char *uid, char *basedn, char *objclass, char *cn, char *home, char *principal, char *shell, int no, ...) { va_list args; if (!uid || !basedn || !cn || !home || !shell) @@ -179,13 +179,19 @@ int ceo_add_user(char *uid, char *basedn, char *objclass, char *cn, char *home, LDAPMod *mods[16]; int i = -1; int ret = 0; + int classes = 4; mods[++i] = xmalloc(sizeof(LDAPMod)); mods[i]->mod_op = LDAP_MOD_ADD; mods[i]->mod_type = "objectClass"; - char *objectClasses[] = { "top", "account", "posixAccount", "shadowAccount", NULL, NULL }; + char *objectClasses[] = { "top", "account", "posixAccount", "shadowAccount", NULL, NULL, NULL, NULL }; if (objclass != NULL) - objectClasses[4] = objclass; + objectClasses[classes++] = objclass; + if (principal) { + objectClasses[classes++] = "krbPrincipalAux"; + objectClasses[classes++] = "krbTicketPolicyAux"; + + } mods[i]->mod_values = objectClasses; mods[++i] = xmalloc(sizeof(LDAPMod)); @@ -225,6 +231,14 @@ int ceo_add_user(char *uid, char *basedn, char *objclass, char *cn, char *home, char *homeDirectory[] = { home, NULL }; mods[i]->mod_values = homeDirectory; + if (principal) { + mods[++i] = xmalloc(sizeof(LDAPMod)); + mods[i]->mod_op = LDAP_MOD_ADD; + mods[i]->mod_type = "krbPrincipalName"; + char *krbPrincipalName[] = { principal, NULL }; + mods[i]->mod_values = krbPrincipalName; + } + va_start(args, no); char *attr; while ((attr = va_arg(args, char *))) { @@ -277,7 +291,7 @@ int ceo_new_uid(int min, int max) { continue; snprintf(filter, sizeof(filter), "(|(uidNumber=%d)(gidNumber=%d))", i, i); - if (ldap_search_s(ld, users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 1, &res) != LDAP_SUCCESS) { + if (ldap_search_s(ld, ldap_users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 1, &res) != LDAP_SUCCESS) { ldap_err("firstuid"); return -1; } @@ -306,7 +320,7 @@ int ceo_user_exists(char *uid) { snprintf(filter, sizeof(filter), "uid=%s", uid); - if (ldap_search_s(ld, users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) { + if (ldap_search_s(ld, ldap_users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) { ldap_err("user_exists"); return -1; } @@ -328,7 +342,7 @@ int ceo_group_exists(char *cn) { snprintf(filter, sizeof(filter), "cn=%s", cn); - if (ldap_search_s(ld, groups_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) { + if (ldap_search_s(ld, ldap_groups_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) { ldap_err("group_exists"); return -1; } @@ -362,22 +376,18 @@ void ceo_ldap_init() { int proto = LDAP_DEFAULT_PROTOCOL; const char *sasl_mech = "GSSAPI"; - if (!admin_bind_userid || !admin_bind_keytab) + if (!ldap_admin_principal) fatal("not configured"); - if (ldap_initialize(&ld, server_url) != LDAP_SUCCESS) + if (ldap_initialize(&ld, ldap_server_url) != LDAP_SUCCESS) ldap_fatal("ldap_initialize"); if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) != LDAP_OPT_SUCCESS) ldap_fatal("ldap_set_option"); - ceo_krb5_auth(admin_bind_userid, admin_bind_keytab); - if (ldap_sasl_interactive_bind_s(ld, NULL, sasl_mech, NULL, NULL, LDAP_SASL_QUIET, &ldap_sasl_interact, NULL) != LDAP_SUCCESS) ldap_fatal("Bind failed"); - - ceo_krb5_deauth(); } void ceo_ldap_cleanup() { diff --git a/src/ldap.h b/src/ldap.h index b29b68b..5a2d3f5 100644 --- a/src/ldap.h +++ b/src/ldap.h @@ -1,6 +1,6 @@ #define LDAP_DEFAULT_PROTOCOL LDAP_VERSION3 -int ceo_add_user(char *, char *, char *, char *, char *, char *, int, ...); +int ceo_add_user(char *, char *, char *, char *, char *, char *, char *, int, ...); int ceo_add_group(char *, char *, int); int ceo_add_group_sudo(char *, char *); int ceo_new_uid(int, int); diff --git a/src/net.c b/src/net.c new file mode 100644 index 0000000..d5db0ad --- /dev/null +++ b/src/net.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include + +#include "util.h" +#include "net.h" +#include "gss.h" +#include "strbuf.h" + +struct strbuf fqdn = STRBUF_INIT; + +const size_t MAX_MSGLEN = 65536; +const size_t MSG_BUFINC = 4096; + +void setup_fqdn(void) { + struct utsname uts; + struct hostent *lo; + + if (uname(&uts)) + fatalpe("uname"); + lo = gethostbyname(uts.nodename); + if (!lo) + fatalpe("gethostbyname"); + + strbuf_addstr(&fqdn, lo->h_name); +} + +void free_fqdn(void) { + strbuf_release(&fqdn); +} + +static size_t recv_one_message(int sock, struct sctp_meta *msg_meta, struct strbuf *msg, int *notification) { + size_t len = 0; + int flags = 0; + int bytes; + + strbuf_reset(msg); + + do { + msg_meta->fromlen = sizeof(msg_meta->from); + + bytes = sctp_recvmsg(sock, msg->buf + len, strbuf_avail(msg) - len, + (sa *)&msg_meta->from, &msg_meta->fromlen, &msg_meta->sinfo, &flags); + + if (bytes < 0) { + if (errno == EAGAIN) + continue; + fatalpe("sctp_recvmsg"); + } + if (!bytes) + break; + len += bytes; + + if (msg->len > MAX_MSGLEN) + fatal("oversized message received"); + if (strbuf_avail(msg) < MSG_BUFINC) + strbuf_grow(msg, MSG_BUFINC); + + } while (~flags & MSG_EOR); + + if (!bytes && len) + fatalpe("EOF in the middle of a message"); + + *notification = flags & MSG_NOTIFICATION; + if (*notification) { + notification_dbg(msg->buf); + union sctp_notification *sn = (union sctp_notification *) msg->buf; + switch (sn->sn_header.sn_type) { + case SCTP_SHUTDOWN_EVENT: + fatal("connection shut down"); + break; + case SCTP_ASSOC_CHANGE: + switch (sn->sn_assoc_change.sac_state) { + case SCTP_COMM_LOST: + fatal("connection lost"); + break; + case SCTP_SHUTDOWN_COMP: + fatal("shutdown complete"); + break; + case SCTP_CANT_STR_ASSOC: + fatal("connection failed (is ceod running?)"); + break; + } + break; + } + } + + strbuf_setlen(msg, len); + return len; +} + +int receive_one_message(int sock, struct sctp_meta *msg_meta, struct strbuf *msg) { + int notification = 0; + + do { + recv_one_message(sock, msg_meta, msg, ¬ification); + } while (notification); + + return msg->len > 0; +} + +void notification_dbg(char *notification) { + union sctp_notification *sn = (union sctp_notification *) notification; + char *extra; + + switch (sn->sn_header.sn_type) { + case SCTP_ASSOC_CHANGE: + extra = "unknown state"; + switch (sn->sn_assoc_change.sac_state) { + case SCTP_COMM_UP: extra = "established"; break; + case SCTP_COMM_LOST: extra = "lost"; break; + case SCTP_RESTART: extra = "restarted"; break; + case SCTP_SHUTDOWN_COMP: extra = "completed shutdown"; break; + case SCTP_CANT_STR_ASSOC: extra = "cannot start"; break; + } + debug("association changed: association 0x%x %s", sn->sn_assoc_change.sac_assoc_id, extra); + break; + case SCTP_PEER_ADDR_CHANGE: + extra = "unknown state"; + switch (sn->sn_paddr_change.spc_state) { + case SCTP_ADDR_AVAILABLE: extra = "unavailable"; break; + case SCTP_ADDR_UNREACHABLE: extra = "unreachable"; break; + case SCTP_ADDR_REMOVED: extra = "removed"; break; + case SCTP_ADDR_ADDED: extra = "added"; break; + case SCTP_ADDR_MADE_PRIM: extra = "made primary"; break; +#ifdef SCTP_ADDR_CONFIRMED + case SCTP_ADDR_CONFIRMED: extra = "confirmed"; break; +#endif + } + + struct sockaddr_in *sa = (struct sockaddr_in *) &sn->sn_paddr_change.spc_aaddr; + char addr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &sa->sin_addr, addr, sizeof(addr)); + debug("peer address change: remote address %s %s", addr, extra); + break; + case SCTP_REMOTE_ERROR: + debug("remote error: association=0x%x error=0x%x", + sn->sn_remote_error.sre_assoc_id, + sn->sn_remote_error.sre_error); + break; + case SCTP_SEND_FAILED: + debug("send failed: association=0x%x error=0x%x", + sn->sn_send_failed.ssf_assoc_id, + sn->sn_send_failed.ssf_error); + break; + case SCTP_ADAPTATION_INDICATION: + debug("adaptation indication: 0x%x", + sn->sn_adaptation_event.sai_adaptation_ind); + break; + case SCTP_PARTIAL_DELIVERY_EVENT: + extra = "unknown indication"; + switch (sn->sn_pdapi_event.pdapi_indication) { + case SCTP_PARTIAL_DELIVERY_ABORTED: + extra = "partial delivery aborted"; + break; + } + debug("partial delivery event: %s", extra); + break; + case SCTP_SHUTDOWN_EVENT: + debug("association 0x%x was shut down", + sn->sn_shutdown_event.sse_assoc_id); + break; + default: + debug("unknown sctp notification type 0x%x\n", + sn->sn_header.sn_type); + } +} diff --git a/src/net.h b/src/net.h new file mode 100644 index 0000000..8674a82 --- /dev/null +++ b/src/net.h @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include + +#if ! defined(SCTP_ADDR_CONFIRMED) && defined(__linux__) +#define SCTP_ADDR_CONFIRMED 5 +#endif + +void notification_dbg(char *); + +#ifdef SCTP_ADAPTION_LAYER +#define sctp_adaptation_layer_event sctp_adaption_layer_event +#define sn_adaptation_event sn_adaption_event +#define sai_adaptation_ind sai_adaption_ind +#define SCTP_ADAPTATION_INDICATION SCTP_ADAPTION_INDICATION +#endif + +typedef struct sockaddr sa; + +extern struct strbuf fqdn; +extern void setup_fqdn(void); +extern void free_fqdn(void); + +struct sctp_meta { + struct sockaddr_storage from; + socklen_t fromlen; + struct sctp_sndrcvinfo sinfo; +}; + +enum { + MSG_AUTH = 0x8000000, + MSG_EXPLODE = 0x8000001, +}; + +#ifdef MSG_ABORT +#define SCTP_ABORT MSG_ABORT +#define SCTP_EOF MSG_EOF +#endif + +#define EKERB -2 +#define ELDAP -3 +#define EHOME -4 + +int receive_one_message(int sock, struct sctp_meta *msg_meta, struct strbuf *msg); diff --git a/src/op-adduser.c b/src/op-adduser.c new file mode 100644 index 0000000..b1465cc --- /dev/null +++ b/src/op-adduser.c @@ -0,0 +1,305 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "net.h" +#include "ceo.pb-c.h" +#include "config.h" +#include "gss.h" +#include "krb5.h" +#include "ldap.h" +#include "homedir.h" +#include "kadm.h" +#include "daemon.h" +#include "strbuf.h" + +char *prog; + +static const int MAX_MESSAGES = 32; +static const int MAX_MESGSIZE = 512; + +char *user_types[] = { + [CEO__ADD_USER__TYPE__MEMBER] = "member", + [CEO__ADD_USER__TYPE__CLUB] = "club", +}; + +Ceo__AddUserResponse *response_create(void) { + Ceo__AddUserResponse *r = xmalloc(sizeof(Ceo__AddUserResponse)); + ceo__add_user_response__init(r); + r->n_messages = 0; + r->messages = xmalloc(MAX_MESSAGES * sizeof(Ceo__StatusMessage *)); + return r; +} + +int32_t response_message(Ceo__AddUserResponse *r, int32_t status, char *fmt, ...) { + va_list args; + Ceo__StatusMessage *statusmsg = xmalloc(sizeof(Ceo__StatusMessage)); + char *message = xmalloc(MAX_MESGSIZE); + + va_start(args, fmt); + vsnprintf(message, MAX_MESGSIZE, fmt, args); + va_end(args); + + ceo__status_message__init(statusmsg); + statusmsg->status = status; + statusmsg->message = message; + + if (r->n_messages >= MAX_MESSAGES) + fatal("too many messages"); + r->messages[r->n_messages++] = statusmsg; + + if (status) + error("%s", message); + else + notice("%s", message); + + return status; +} + +void response_delete(Ceo__AddUserResponse *r) { + int i; + + for (i = 0; i < r->n_messages; i++) { + free(r->messages[i]->message); + free(r->messages[i]); + } + free(r->messages); + free(r); +} + + +static int check_adduser(Ceo__AddUser *in, Ceo__AddUserResponse *out, char *client) { + int office = check_group(client, "office"); + int syscom = check_group(client, "syscom"); + + notice("adding uid=%s cn=%s by %s", in->username, in->realname, client); + + if (!office && !syscom) + return response_message(out, EPERM, "%s not authorized to create users", client); + + if (!in->username) + return response_message(out, EINVAL, "missing required argument: username"); + if (!in->realname) + return response_message(out, EINVAL, "missing required argument: realname"); + + if (in->type == CEO__ADD_USER__TYPE__MEMBER) { + if (!in->password) + return response_message(out, EINVAL, "missing required argument: password"); + } else if (in->type == CEO__ADD_USER__TYPE__CLUB) { + if (in->password) + return response_message(out, EINVAL, "club accounts cannot have passwords"); + if (in->program) + return response_message(out, EINVAL, "club accounts cannot have programs"); + } else { + return response_message(out, EINVAL, "invalid user type: %d", in->type); + } + + if (getpwnam(in->username) != NULL) + return response_message(out, EEXIST, "user %s already exists", in->username); + + if (getgrnam(in->username) != NULL) + return response_message(out, EEXIST, "group %s already exists", in->username); + + if (ceo_user_exists(in->username)) + return response_message(out, EEXIST, "user %s already exists in LDAP", in->username); + + if (ceo_group_exists(in->username)) + return response_message(out, EEXIST, "group %s already exists in LDAP", in->username); + + return 0; +} + +static void adduser_spam(Ceo__AddUser *in, Ceo__AddUserResponse *out, char *client, char *prog, int status) { + char *argv[] = { + notify_hook, prog, client, + in->username, in->realname, in->program ?: "", + status ? "failure" : "success", NULL + }; + + struct strbuf message = STRBUF_INIT; + for (int i = 0; i < out->n_messages; i++) + strbuf_addf(&message, "%s\n", out->messages[i]->message); + + spawnv_msg(notify_hook, argv, &message); + strbuf_release(&message); +} + +static int32_t addmember(Ceo__AddUser *in, Ceo__AddUserResponse *out) { + char homedir[1024]; + char principal[1024]; + int user_stat, group_stat, krb_stat, home_stat; + int id; + + if (snprintf(principal, sizeof(principal), "%s@%s", + in->username, krb5_realm) >= sizeof(principal)) + fatal("principal overflow"); + + if (snprintf(homedir, sizeof(homedir), "%s/%s", + member_home, in->username) >= sizeof(homedir)) + fatal("homedir overflow"); + + if ((id = ceo_new_uid(member_min_id, member_max_id)) <= 0) + fatal("no available uids in range [%ld, %ld]", member_min_id, member_max_id); + + if ((krb_stat = ceo_del_princ(in->username))) + return response_message(out, EEXIST, "unable to overwrite orphaned kerberos principal %s", in->username); + + if ((user_stat = ceo_add_user(in->username, ldap_users_base, "member", in->realname, homedir, principal, + member_shell, id, "program", in->program, NULL))) + return response_message(out, ELDAP, "unable to create ldap account %s", in->username); + response_message(out, 0, "successfully created ldap account"); + + /* errors that occur after this point are not fatal */ + + if ((krb_stat = ceo_add_princ(in->username, in->password))) + return response_message(out, EKERB, "unable to create kerberos principal %s", in->username); + response_message(out, 0, "successfully created principal"); + + if ((group_stat = ceo_add_group(in->username, ldap_groups_base, id))) + response_message(out, ELDAP, "unable to create ldap group %s", in->username); + else + response_message(out, 0, "successfully created ldap group"); + + if ((home_stat = ceo_create_home(homedir, member_home_skel, id, id))) + response_message(out, EHOME, "unable to create home directory for %s", in->username); + else + response_message(out, 0, "successfully created home directory"); + + + return krb_stat || user_stat || group_stat || home_stat; +} + +static int32_t addclub(Ceo__AddUser *in, Ceo__AddUserResponse *out) { + char homedir[1024]; + int krb_stat, user_stat, group_stat, sudo_stat, home_stat; + int id; + + if (snprintf(homedir, sizeof(homedir), "%s/%s", + club_home, in->username) >= sizeof(homedir)) + fatal("homedir overflow"); + + if ((id = ceo_new_uid(club_min_id, club_max_id)) <= 0) + fatal("no available uids in range [%ld, %ld]", club_min_id, club_max_id); + + if ((krb_stat = ceo_del_princ(in->username))) + return response_message(out, EKERB, "unable to clear principal %s", in->username); + + if ((user_stat = ceo_add_user(in->username, ldap_users_base, "club", in->realname, homedir, + NULL, club_shell, id, NULL))) + return response_message(out, ELDAP, "unable to create ldap account %s", in->username); + response_message(out, 0, "successfully created ldap account"); + + /* errors that occur after this point are not fatal */ + + if ((group_stat = ceo_add_group(in->username, ldap_groups_base, id))) + response_message(out, ELDAP, "unable to create ldap group %s", in->username); + else + response_message(out, 0, "successfully created ldap group"); + + if ((sudo_stat = ceo_add_group_sudo(in->username, ldap_sudo_base))) + response_message(out, ELDAP, "unable to create ldap sudoers %s", in->username); + else + response_message(out, 0, "successfully created ldap sudoers"); + + if ((home_stat = ceo_create_home(homedir, club_home_skel, id, id))) + response_message(out, EHOME, "unable to create home directory for %s", in->username); + else + response_message(out, 0, "successfully created home directory"); + + return user_stat || group_stat || sudo_stat || home_stat; +} + +static int32_t adduser(Ceo__AddUser *in, Ceo__AddUserResponse *out, char *client) { + int32_t chk_stat, status; + char *prog; + + chk_stat = check_adduser(in, out, client); + if (chk_stat) + return chk_stat; + + if (in->type == CEO__ADD_USER__TYPE__MEMBER) { + status = addmember(in, out); + prog = "addmember"; + } else if (in->type == CEO__ADD_USER__TYPE__CLUB) { + status = addclub(in, out); + prog = "addclub"; + } else { + fatal("unknown user type %d", in->type); + } + + if (status) + response_message(out, 0, "there were failures, please contact systems committee"); + + adduser_spam(in, out, client, prog, status); + + return status; +} + +void cmd_adduser(void) { + Ceo__AddUser *in_proto; + Ceo__AddUserResponse *out_proto = response_create(); + struct strbuf in = STRBUF_INIT; + struct strbuf out = STRBUF_INIT; + + if (strbuf_read(&in, STDIN_FILENO, 0) < 0) + fatalpe("read"); + + in_proto = ceo__add_user__unpack(&protobuf_c_default_allocator, + in.len, (uint8_t *)in.buf); + if (!in_proto) + fatal("malformed add user message"); + + char *client = getenv("CEO_USER"); + if (!client) + fatal("environment variable CEO_USER is not set"); + + adduser(in_proto, out_proto, client); + + strbuf_grow(&out, ceo__add_user_response__get_packed_size(out_proto)); + strbuf_setlen(&out, ceo__add_user_response__pack(out_proto, (uint8_t *)out.buf)); + full_write(STDOUT_FILENO, out.buf, out.len); + + ceo__add_user__free_unpacked(in_proto, &protobuf_c_default_allocator); + response_delete(out_proto); + + strbuf_release(&in); + strbuf_release(&out); +} + +int main(int argc, char *argv[]) { + prog = xstrdup(basename(argv[0])); + init_log(prog, LOG_PID, LOG_AUTHPRIV); + + configure(); + + if (setenv("KRB5CCNAME", "MEMORY:adduser", 1)) + fatalpe("setenv"); + + ceo_krb5_init(); + ceo_krb5_auth(ldap_admin_principal); + ceo_ldap_init(); + ceo_kadm_init(); + + cmd_adduser(); + + ceo_kadm_cleanup(); + ceo_ldap_cleanup(); + ceo_krb5_deauth(); + ceo_krb5_cleanup(); + + free_config(); + free(prog); + + return 0; +} diff --git a/src/ops.c b/src/ops.c new file mode 100644 index 0000000..b86ef43 --- /dev/null +++ b/src/ops.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include + +#include "strbuf.h" +#include "ops.h" +#include "net.h" +#include "util.h" +#include "config.h" + +static struct op *ops; + +static const char *default_op_dir = "/usr/lib/ceod"; +static const char *op_dir; + +static void add_op(char *host, char *name, uint32_t id) { + struct op *new = xmalloc(sizeof(struct op)); + errno = 0; + new->next = ops; + new->name = xstrdup(name); + new->id = id; + new->path = NULL; + + struct hostent *hostent = gethostbyname(host); + if (!hostent) + badconf("cannot add op %s: %s: %s", name, host, hstrerror(h_errno)); + new->hostname = strdup(hostent->h_name); + new->local = !strcmp(fqdn.buf, hostent->h_name); + new->addr = *(struct in_addr *)hostent->h_addr_list[0]; + + if (new->local) { + new->path = xmalloc(strlen(op_dir) + strlen("/op-") + strlen(name) + 1); + sprintf(new->path, "%s/op-%s", op_dir, name); + if (access(new->path, X_OK)) + fatalpe("cannot add op: %s: %s", name, new->path); + } + + ops = new; + debug("added op %s (%s%s)", new->name, new->local ? "" : "on ", + new->local ? "local" : host); +} + +struct op *get_local_op(uint32_t id) { + for (struct op *op = ops; op; op = op->next) { + if (op->local && op->id == id) + return op; + } + return NULL; +} + +struct op *find_op(const char *name) { + for (struct op *op = ops; op; op = op->next) { + if (!strcmp(name, op->name)) + return op; + } + return NULL; +} + +void setup_ops(void) { + char op_config_dir[1024]; + DIR *dp; + struct dirent *de; + struct strbuf line = STRBUF_INIT; + unsigned lineno = 0; + unsigned op_count = 0; + + op_dir = getenv("CEO_LIB_DIR") ?: default_op_dir; + + if (snprintf(op_config_dir, sizeof(op_config_dir), "%s/%s", config_dir, "ops") >= sizeof(op_config_dir)) + fatal("ops dir path too long"); + + dp = opendir(op_config_dir); + if (!dp) + fatalpe("opendir: %s", op_config_dir); + + while ((de = readdir(dp))) { + FILE *fp = fopenat(dp, de->d_name, O_RDONLY); + if (!fp) + warnpe("open: %s/%s", op_config_dir, de->d_name); + while (strbuf_getline(&line, fp, '\n') != EOF) { + lineno++; + strbuf_trim(&line); + + if (!line.len || line.buf[0] == '#') + continue; + + struct strbuf **words = strbuf_splitws(&line); + + if (strbuf_list_len(words) != 3) + badconf("%s/%s: expected three words on line %d", op_config_dir, de->d_name, lineno); + + errno = 0; + char *end; + int id = strtol(words[2]->buf, &end, 0); + if (errno || *end) + badconf("%s/%s: invalid id '%s' on line %d", op_config_dir, de->d_name, words[2]->buf, lineno); + + add_op(words[0]->buf, words[1]->buf, id); + op_count++; + + strbuf_list_free(words); + } + fclose(fp); + } + + closedir(dp); + strbuf_release(&line); +} + +void free_ops(void) { + while (ops) { + struct op *next = ops->next; + free(ops->name); + free(ops->hostname); + free(ops->path); + free(ops); + ops = next; + } +} diff --git a/src/ops.h b/src/ops.h new file mode 100644 index 0000000..3e7465c --- /dev/null +++ b/src/ops.h @@ -0,0 +1,14 @@ +struct op { + char *name; + uint32_t id; + int local; + char *hostname; + char *path; + struct in_addr addr; + struct op *next; +}; + +void setup_ops(void); +void free_ops(void); +struct op *find_op(const char *name); +struct op *get_local_op(uint32_t id); diff --git a/src/parser.c b/src/parser.c index 9b8d3e3..7d462ea 100644 --- a/src/parser.c +++ b/src/parser.c @@ -53,7 +53,7 @@ static void parse_name(struct config_file *file, char *name, size_t maxlen) { break; } - if (!isalpha(c) && c != '_' && c != '-') { + if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') { unparse_char(file, c); break; } diff --git a/src/util.c b/src/util.c index 656b587..6b0a5b4 100644 --- a/src/util.c +++ b/src/util.c @@ -1,10 +1,13 @@ +#define _ATFILE_SOURCE #include #include #include #include #include +#include #include #include +#include #include "util.h" #include "strbuf.h" @@ -26,6 +29,8 @@ static void errmsg(int prio, const char *prefix, const char *fmt, va_list args) syslog(prio, "%s", msg.buf); if (log_stderr) fputs(msg.buf, stderr); + + strbuf_release(&msg); } static void errmsgpe(int prio, const char *prefix, const char *fmt, va_list args) { @@ -38,6 +43,8 @@ static void errmsgpe(int prio, const char *prefix, const char *fmt, va_list args syslog(prio, "%s", msg.buf); if (log_stderr) fputs(msg.buf, stderr); + + strbuf_release(&msg); } NORETURN static void die(int prio, const char *prefix, const char *msg, va_list args) { @@ -148,3 +155,127 @@ int spawnv(const char *path, char *const argv[]) { exit(execv(path, argv)); return status; } + +void full_write(int fd, const void *buf, size_t count) { + ssize_t total = 0; + + while (total < count) { + ssize_t wcount = write(fd, (char *)buf + total, count - total); + if (wcount < 0) + fatalpe("write"); + total += wcount; + } +} + +int spawnvem(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr) { + int pid, wpid, status; + int tochild[2]; + int fmchild[2]; + + if (pipe(tochild)) + fatalpe("pipe"); + if (pipe(fmchild)) + fatalpe("pipe"); + + fflush(stdout); + fflush(stderr); + + pid = fork(); + if (pid < 0) + fatalpe("fork"); + if (!pid) { + dup2(tochild[0], STDIN_FILENO); + dup2(fmchild[1], STDOUT_FILENO); + if (cap_stderr) + dup2(STDOUT_FILENO, STDERR_FILENO); + close(tochild[0]); + close(tochild[1]); + close(fmchild[0]); + close(fmchild[1]); + execve(path, argv, envp); + fatalpe("execve"); + } else { + close(tochild[0]); + close(fmchild[1]); + full_write(tochild[1], output->buf, output->len); + close(tochild[1]); + + if (input) + strbuf_read(input, fmchild[0], 0); + close(fmchild[0]); + } + + wpid = waitpid(pid, &status, 0); + if (wpid < 0) + fatalpe("waitpid"); + else if (wpid != pid) + fatal("waitpid is broken"); + + if (WIFEXITED(status) && WEXITSTATUS(status)) + notice("child %s exited with status %d", path, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + notice("child %s killed by signal %d", path, WTERMSIG(status)); + + return status; +} + +int spawnv_msg(const char *path, char *const *argv, const struct strbuf *output) { + return spawnvem(path, argv, environ, output, NULL, 0); +} + +int check_group(char *username, char *group) { + struct group *grp = getgrnam(group); + char **members; + + if (grp) + for (members = grp->gr_mem; *members; members++) + if (!strcmp(username, *members)) + return 1; + + return 0; +} + +FILE *fopenat(DIR *d, const char *path, int flags) { + int dfd = dirfd(d); + if (dfd < 0) + return NULL; + int fd = openat(dfd, path, flags); + if (fd < 0) + return NULL; + return fdopen(fd, flags & O_RDWR ? "r+" : + flags & O_WRONLY ? "w" : + "r"); +} + +void make_env(char **envp, ...) { + const size_t len = 4096; + size_t used = 0; + int args = 0; + char *buf = xmalloc(len); + va_list ap; + va_start(ap, envp); + char *name, *val; + + while ((name = va_arg(ap, char *))) { + val = va_arg(ap, char *); + if (!val) + continue; + int n = snprintf(buf + used, len - used, "%s=%s", name, val); + if (n < 0) + fatalpe("snprintf"); + if (n >= len - used) + fatal("environment too big"); + + envp[args++] = buf + used; + used += n + 1; + } + + if (!args) + free(buf); + + envp[args] = NULL; +} + +void free_env(char **envp) { + free(*envp); +} diff --git a/src/util.h b/src/util.h index 5d2757f..70fc82d 100644 --- a/src/util.h +++ b/src/util.h @@ -2,8 +2,14 @@ #define CEO_UTIL_H #include +#include #include #include +#include +#include +#include + +#include "strbuf.h" #ifdef __GNUC__ #define NORETURN __attribute__((__noreturn__)) @@ -17,8 +23,18 @@ #define LOG_AUTHPRIV LOG_AUTH #endif +extern char **environ; + int spawnv(const char *path, char *const *argv); +int spawnv_msg(const char *path, char *const *argv, const struct strbuf *output); +int spawnvem(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr); +void full_write(int fd, const void *buf, size_t count); +ssize_t full_read(int fd, void *buf, size_t len); +FILE *fopenat(DIR *d, const char *path, int flags); +void make_env(char **envp, ...); +void free_env(char **envp); void init_log(const char *ident, int option, int facility); +int check_group(char *username, char *group); PRINTF_LIKE(0) NORETURN void fatal(const char *, ...); PRINTF_LIKE(0) NORETURN void fatalpe(const char *, ...); @@ -59,7 +75,6 @@ static inline void *xcalloc(size_t nmemb, size_t size) { return alloc; } - static inline char *xstrdup(const char *s) { char *dup = strdup(s); diff --git a/src/zfsaddhomedir.c b/src/zfsaddhomedir.c deleted file mode 100644 index ae556f6..0000000 --- a/src/zfsaddhomedir.c +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "util.h" - -int main(int argc, char *argv[]) { - if(argc < 7) { - fprintf(stderr, "Usage: zfsaddhomedir homedir refquota skeldir uid gid mode acl\n"); - return 1; - } - - char *homedir = argv[1]; - char *skeldir = argv[3]; - char refquota[32]; - char *mode = argv[6]; - char *acl = (argc >= 8) ? argv[7] : NULL; - uid_t uid, gid; - char *zfs_bin = "/usr/sbin/zfs"; - char *chmod_bin = "/usr/bin/chmod"; - char *dataset = homedir + 1; - char *create_argv[] = { "zfs", "create", dataset, NULL }; - char *quota_argv[] = { "zfs", "set", refquota, dataset, NULL }; - char *mode_argv[] = { "chmod", mode, homedir, NULL }; - char *acl_argv[] = { "chmod", acl, homedir, NULL }; - DIR *skel; - struct dirent *skelent; - - assert(homedir[0]); - uid = atol(argv[4]); - gid = atol(argv[5]); - snprintf(refquota, sizeof(refquota), "refquota=%s", argv[2]); - - if(spawnv(zfs_bin, create_argv)) - return 1; - if(spawnv(zfs_bin, quota_argv)) - return 1; - if(spawnv(chmod_bin, mode_argv)) - return 1; - if(acl && spawnv(chmod_bin, acl_argv)) - return 1; - - skel = opendir(skeldir); - if (!skel) { - errorpe("failed to open %s", skeldir); - return -1; - } - - while ((skelent = readdir(skel))) { - struct stat sb; - char src[PATH_MAX], dest[PATH_MAX]; - - if (!strcmp(skelent->d_name, ".") || !strcmp(skelent->d_name, "..")) - continue; - - snprintf(src, sizeof(src), "%s/%s", skeldir, skelent->d_name); - snprintf(dest, sizeof(dest), "/%s/%s", homedir, skelent->d_name); - lstat(src, &sb); - - if (sb.st_uid || sb.st_gid) { - warn("not creating %s due to ownership", dest); - continue; - } - - if (S_ISREG(sb.st_mode)) { - int bytes; - char buf[4096]; - - int srcfd = open(src, O_RDONLY); - if (srcfd == -1) { - warnpe("open: %s", src); - continue; - } - - int destfd = open(dest, O_WRONLY|O_CREAT|O_EXCL, sb.st_mode & 0777); - if (destfd == -1) { - warnpe("open: %s", dest); - close(srcfd); - continue; - } - - for (;;) { - bytes = read(srcfd, buf, sizeof(buf)); - if (!bytes) - break; - if (bytes < 0) { - warnpe("read"); - break; - } - if (write(destfd, buf, bytes) < 0) { - warnpe("write"); - break; - } - } - if (fchown(destfd, uid, gid)) - errorpe("chown: %s", dest); - - close(srcfd); - close(destfd); - } else if (S_ISDIR(sb.st_mode)) { - if (mkdir(dest, sb.st_mode & 0777)) { - warnpe("mkdir: %s", dest); - continue; - } - if (chown(dest, uid, gid)) - errorpe("chown: %s", dest); - } else if (S_ISLNK(sb.st_mode)) { - char lnkdest[PATH_MAX]; - int bytes; - bytes = readlink(src, lnkdest, sizeof(lnkdest)); - lnkdest[bytes] = '\0'; - if (bytes == -1) { - warnpe("readlink: %s", src); - continue; - } - if (symlink(lnkdest, dest)) { - warnpe("symlink: %s", dest); - continue; - } - if (lchown(dest, uid, gid)) - errorpe("lchown: %s", dest); - } else { - warn("not creating %s", dest); - } - } - - closedir(skel); - - if (chown(homedir, uid, gid)) { - errorpe("failed to chown %s", homedir); - return -1; - } - - return 0; -}