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', '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
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:
}
"""
- 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):
}
"""
- 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])
]
"""
- 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])
]
"""
- 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])
]
"""
- 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']:
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)
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 = ()
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 = []
### 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:
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 ]) ])
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 ]
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 ]
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=([^,]*)')
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)
-etc/accounts.cf etc/kerberos.cf etc/ldap.cf etc/csc
+etc/accounts.cf etc/library.cf etc/ops etc/spam etc/csc
# /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 =
+member_home_acl = "u::rwx,g::rx,o::rx"
+member_home_skel = "/users/skel"
### Club Account Options ###
club_shell = "/bin/bash"
club_home = "/users"
club_home_acl = "A+group:%s:rwpRAxaWdDcCs:fd:allow"
+club_home_skel = "/users/skel"
-### Administrative Account Options
+### Administrative Account Options ###
admin_min_id = 10001
admin_max_id = 19999
-admin_shell = "/bin/bash"
-admin_home = "/users"
-### Home Directory Options ###
+### 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 ###
-skeleton_dir = "/users/skel"
-homedir_mode = "0755"
-refquota = "4G"
+notify_hook = "/etc/csc/spam/new-member"
+expire_hook = "/etc/csc/spam/expired-account"
-### Validation Tuning ###
+### Miscellaneous ###
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"
+mathsoc_dont_count = "cpdohert dlgawley dtbartle mbiggs saforres tmyklebu mgregson rridge dbelange"
+++ /dev/null
-# /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"
+++ /dev/null
-# /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"
--- /dev/null
+# /etc/csc/library.cf: Library Config
+
+library_connect_string = "postgres://librarian:PWPWPWPWPWPWPWPWPWPW@127.0.0.1/library"
+aws_account_key = "KEYKEYKEYKEYKEYKEYKY"
--- /dev/null
+ginseng adduser 0x01
--- /dev/null
+#!/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"
--- /dev/null
+#!/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"
-CONFIG_STR(server_url)
-
-CONFIG_STR(users_base)
-CONFIG_STR(groups_base)
-CONFIG_STR(sudo_base)
-
-CONFIG_STR(skeleton_dir)
-
CONFIG_STR(member_shell)
CONFIG_INT(member_min_id)
CONFIG_INT(member_max_id)
CONFIG_STR(member_home)
+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_skel)
CONFIG_STR(notify_hook)
-CONFIG_STR(realm)
+CONFIG_STR(krb5_realm)
+CONFIG_STR(krb5_admin_principal)
-CONFIG_STR(admin_principal)
-CONFIG_STR(admin_bind_userid)
+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)
#include "util.h"
#include "config.h"
-int ceo_create_home(char *homedir, uid_t uid, gid_t gid) {
+int ceo_create_home(char *homedir, char *skel, uid_t uid, gid_t gid) {
int mask;
- DIR *skel;
+ DIR *skeldir;
struct dirent *skelent;
mask = umask(0);
return -1;
}
- skel = opendir(skeleton_dir);
- if (!skel) {
- errorpe("failed to open %s", skeleton_dir);
+ skeldir = opendir(skel);
+ if (!skeldir) {
+ errorpe("failed to open %s", skel);
return -1;
}
- while ((skelent = readdir(skel))) {
+ 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", skeleton_dir, 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);
}
}
- closedir(skel);
+ closedir(skeldir);
if (chown(homedir, uid, gid)) {
errorpe("failed to chown %s", homedir);
#include <sys/acl.h>
-int ceo_create_home(char *homedir, uid_t uid, gid_t gid);
+int ceo_create_home(char *homedir, char *skel, uid_t uid, gid_t gid);
kadm5_config_params params;
memset((void *) ¶ms, 0, sizeof(params));
- debug("kadmin: initializing using keytab for %s", admin_principal);
+ debug("kadmin: initializing using keytab for %s", krb5_admin_principal);
- retval = kadm5_init_with_skey(admin_principal, NULL,
+ retval = kadm5_init_with_skey(krb5_admin_principal, NULL,
KADM5_ADMIN_SERVICE, ¶ms, KADM5_STRUCT_VERSION,
KADM5_API_VERSION_2, NULL, &handle);
if (retval || !handle) {
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");
}
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");
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;
}
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;
}
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;
}
int proto = LDAP_DEFAULT_PROTOCOL;
const char *sasl_mech = "GSSAPI";
- if (!admin_bind_userid)
+ 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)
return response_message(out, EKERB, "unable to create kerberos principal %s", in->username);
response_message(out, 0, "successfully created principal");
- if ((user_stat = ceo_add_user(in->username, users_base, "member", in->realname, homedir,
+ if ((user_stat = ceo_add_user(in->username, ldap_users_base, "member", in->realname, homedir,
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 ((group_stat = ceo_add_group(in->username, groups_base, id)))
+ 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, id, id)))
+ 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");
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, users_base, "club", in->realname, homedir,
+ if ((user_stat = ceo_add_user(in->username, ldap_users_base, "club", in->realname, homedir,
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, groups_base, id)))
+ 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, sudo_base)))
+ 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, id, id)))
+ 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");
fatalpe("setenv");
ceo_krb5_init();
- ceo_krb5_auth(admin_bind_userid);
+ ceo_krb5_auth(ldap_admin_principal);
ceo_ldap_init();
ceo_kadm_init();
op_dir = getenv("CEO_LIB_DIR") ?: default_op_dir;
- if (snprintf(op_config_dir, sizeof(op_config_dir), "%s/%s", config_dir, "ops.d") >= sizeof(op_config_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);