Merge commit 'ceod'

This commit is contained in:
Michael Spang 2009-07-29 23:49:26 -04:00
commit 873f7ac9a6
63 changed files with 2082 additions and 2030 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
/build-stamp
/build
*.pyc
*.cf

View File

@ -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)

7
debian/.gitignore vendored
View File

@ -1,4 +1,9 @@
/ceo.substvars
/ceo
/ceo-common
/ceo-clients
/ceo-daemon
/ceo-gui
/files
/*.debhelper
/*.debhelper.log
/*.substvars

2
debian/ceo-clients.manpages vendored Normal file
View File

@ -0,0 +1,2 @@
docs/addclub.1
docs/addmember.1

1
debian/ceo-common.dirs vendored Normal file
View File

@ -0,0 +1 @@
etc/csc

1
debian/ceo-common.install vendored Normal file
View File

@ -0,0 +1 @@
etc/accounts.cf etc/library.cf etc/ops etc/spam etc/csc

View File

@ -1,3 +1 @@
usr/bin
etc/csc
etc/ldap/schema

1
debian/ceo-daemon.install vendored Normal file
View File

@ -0,0 +1 @@
etc/csc.schema etc/ldap/schema

1
debian/ceo-gui.manpages vendored Normal file
View File

@ -0,0 +1 @@
docs/ceo.1

5
debian/changelog vendored
View File

@ -1,3 +1,8 @@
ceo (0.4.24) stable testing; urgency=low
* Bump standards version.
-- Michael Spang <mspang@uwaterloo.ca> 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.

35
debian/control vendored
View File

@ -2,13 +2,32 @@ Source: ceo
Section: admin
Priority: optional
Maintainer: Systems Committee <syscom@csclub.uwaterloo.ca>
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.

1
debian/docs vendored
View File

@ -1 +0,0 @@
docs/*

2
debian/install vendored
View File

@ -1,2 +0,0 @@
etc/accounts.cf.example etc/kerberos.cf.example etc/ldap.cf.example etc/csc
etc/csc.schema etc/ldap/schema

1
debian/manpages vendored
View File

@ -1 +0,0 @@
docs/*.[0-9]

11
debian/rules vendored
View File

@ -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

52
etc/accounts.cf Normal file
View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

4
etc/library.cf Normal file
View File

@ -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"

View File

@ -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"

1
etc/ops/adduser Normal file
View File

@ -0,0 +1 @@
ginseng adduser 0x01

49
etc/spam/expired-account Executable file
View File

@ -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 <ceo+expired@csclub.uwaterloo.ca>"
echo "Reply-to: CSClub Exec <exec@csclub.uwaterloo.ca>"
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"

71
etc/spam/new-member Executable file
View File

@ -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 <ceo+$prog@csclub.uwaterloo.ca>"
h_to="Membership and Accounts <ceo@csclub.uwaterloo.ca>"
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"

View File

@ -1 +0,0 @@
/* This file left intentionally blank. */

View File

@ -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 <sys/types.h>
#include <gssrpc/rpc.h>
#include <krb5.h>
#include <kdb.h>
#include <com_err.h>
#include <kadm5/kadm_err.h>
#include <kadm5/chpass_util_strings.h>
#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__ */

View File

@ -1 +0,0 @@
/* This file left intentionally blank. */

View File

@ -1,74 +0,0 @@
/*
* ettmp11037.h:
* This file is automatically generated; please do not edit it.
*/
#include <et/com_err.h>
#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

View File

@ -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__ */

6
src/.gitignore vendored
View File

@ -3,5 +3,11 @@
.nfs*
/addmember
/addclub
/adduser
/op-adduser
/zfsaddhomedir
/config-test
/ceod
/ceoc
/ceo.pb-c.c
/ceo.pb-c.h

View File

@ -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

View File

@ -12,159 +12,81 @@
#include <syslog.h>
#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;
}

View File

@ -1,35 +0,0 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/acl.h>
#include <dirent.h>
#include <pwd.h>
#include <fcntl.h>
#include <assert.h>
#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;
}

View File

@ -1,3 +0,0 @@
#include <sys/types.h>
int ceo_create_home(char *, char *, uid_t, gid_t, char *, char *);

View File

@ -12,19 +12,13 @@
#include <syslog.h>
#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;
}

23
src/ceo.proto Normal file
View File

@ -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;
}

173
src/ceoc.c Normal file
View File

@ -0,0 +1,173 @@
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <libgen.h>
#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;
}

View File

@ -1,58 +0,0 @@
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#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);
}

View File

@ -1,3 +0,0 @@
int ceo_get_privileged();
char *ceo_get_user();
void ceo_notify_hook(int, ...);

View File

@ -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)

View File

@ -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;
}
}
}

View File

@ -4,6 +4,7 @@
#undef CONFIG_STR
#undef CONFIG_INT
void configure();
void configure(void);
void free_config(void);
extern const char *config_dir;

10
src/daemon.h Normal file
View File

@ -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);

183
src/dmaster.c Normal file
View File

@ -0,0 +1,183 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <libgen.h>
#include <getopt.h>
#include <errno.h>
#include <netdb.h>
#include <alloca.h>
#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;
}

157
src/dslave.c Normal file
View File

@ -0,0 +1,157 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <libgen.h>
#include <getopt.h>
#include <errno.h>
#include <netdb.h>
#include <alloca.h>
#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);
}

220
src/gss.c Normal file
View File

@ -0,0 +1,220 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <grp.h>
#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;
}

12
src/gss.h Normal file
View File

@ -0,0 +1,12 @@
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_krb5.h>
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);

View File

@ -1,63 +1,43 @@
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/acl.h>
#include <dirent.h>
#include <pwd.h>
#include <fcntl.h>
#include <assert.h>
#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;
}

3
src/homedir.h Normal file
View File

@ -0,0 +1,3 @@
#include <sys/acl.h>
int ceo_create_home(char *homedir, char *skel, uid_t uid, gid_t gid);

View File

@ -14,35 +14,40 @@ void ceo_kadm_init() {
kadm5_config_params params;
memset((void *) &params, 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, &params, 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;

View File

@ -1,4 +1,5 @@
#include <stdio.h>
#include <krb5.h>
#include <syslog.h>
@ -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);
}

View File

@ -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);

View File

@ -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() {

View File

@ -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);

169
src/net.c Normal file
View File

@ -0,0 +1,169 @@
#include <stdio.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#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, &notification);
} 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);
}
}

47
src/net.h Normal file
View File

@ -0,0 +1,47 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <gssapi/gssapi.h>
#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);

305
src/op-adduser.c Normal file
View File

@ -0,0 +1,305 @@
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <syslog.h>
#include <libgen.h>
#include <getopt.h>
#include <errno.h>
#include <netdb.h>
#include <alloca.h>
#include <pwd.h>
#include <grp.h>
#include <sys/wait.h>
#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;
}

121
src/ops.c Normal file
View File

@ -0,0 +1,121 @@
#include <errno.h>
#include <inttypes.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#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;
}
}

14
src/ops.h Normal file
View File

@ -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);

View File

@ -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;
}

View File

@ -1,10 +1,13 @@
#define _ATFILE_SOURCE
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <grp.h>
#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);
}

View File

@ -2,8 +2,14 @@
#define CEO_UTIL_H
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <syslog.h>
#include <sys/types.h>
#include <dirent.h>
#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);

View File

@ -1,139 +0,0 @@
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <assert.h>
#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;
}