diff --git a/.gbp.conf b/.gbp.conf deleted file mode 100644 index ee5f3e154..000000000 --- a/.gbp.conf +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -sign-tags = True -posttag = git push /users/git/public/pyceo.git --tags -debian-tag=v%(version)s diff --git a/.gitignore b/.gitignore index 232247c84..ea3ce8f3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -/build-stamp -/build -*.pyc -/build-ceo -/build-ceod +__pycache__/ +/venv/ +.vscode/ diff --git a/bin/ceo b/bin/ceo deleted file mode 100755 index 32d48f9a2..000000000 --- a/bin/ceo +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/python - -import sys, ldap -from getpass import getpass -import ceo.urwid.main -import ceo.console.main -from ceo import ldapi, members - -def start(): - try: - if len(sys.argv) == 1: - print "Reading config file...", - members.configure() - - print "Connecting to LDAP..." - members.connect(AuthCallback()) - - ceo.urwid.main.start() - else: - members.configure() - members.connect(AuthCallback()) - ceo.console.main.start() - except ldap.LOCAL_ERROR, e: - print ldapi.format_ldaperror(e) - except ldap.INSUFFICIENT_ACCESS, e: - print ldapi.format_ldaperror(e) - print "You probably aren't permitted to do whatever you just tried." - print "Admittedly, ceo probably shouldn't have crashed either." - -class AuthCallback: - def callback(self, error): - try: - print "Password: ", - return getpass("") - except KeyboardInterrupt: - print "" - sys.exit(1) - -if __name__ == '__main__': - start() diff --git a/build.sh b/build.sh deleted file mode 100755 index fbabdcf64..000000000 --- a/build.sh +++ /dev/null @@ -1,5 +0,0 @@ -if test -e .git; then - git-buildpackage --git-ignore-new -us -uc -else - debuild -us -uc -fi diff --git a/ceo_common/interfaces/IFileService.py b/ceo_common/interfaces/IFileService.py new file mode 100644 index 000000000..87d376fcc --- /dev/null +++ b/ceo_common/interfaces/IFileService.py @@ -0,0 +1,24 @@ +from typing import List + +from zope.interface import Interface + + +class IFileService(Interface): + """ + A service which can access, create and modify files on the + NFS users' directory. + """ + + def create_home_dir(username: str, is_club: bool = False): + """ + Create a new home dir for the given user or club. + """ + + def get_forwarding_addresses(username: str) -> List[str]: + """ + Get the contents of the user's ~/.forward file, + one line at a time. + """ + + def set_forwarding_addresses(username: str, addresses: List[str]): + """Set the contents of the user's ~/.forward file.""" diff --git a/ceo_common/interfaces/IGroup.py b/ceo_common/interfaces/IGroup.py index c2daa88ea..2a85a702b 100644 --- a/ceo_common/interfaces/IGroup.py +++ b/ceo_common/interfaces/IGroup.py @@ -25,14 +25,14 @@ class IGroup(Interface): def get_members() -> List[IUser]: """Get a list of the members in this group.""" - def serialize_for_modlist() -> Dict: + def serialize_for_modlist() -> Dict[str, List[bytes]]: """ Serialize this group into a dict to be passed to ldap.modlist.addModlist(). """ # static method - def deserialize_from_dict(data: Dict): + def deserialize_from_dict(data: Dict[str, List[bytes]]): """Deserialize this group from a dict returned by ldap.search_s(). :returns: IGroup diff --git a/ceo_common/interfaces/ILDAPService.py b/ceo_common/interfaces/ILDAPService.py index af3c09260..27509aea1 100644 --- a/ceo_common/interfaces/ILDAPService.py +++ b/ceo_common/interfaces/ILDAPService.py @@ -10,18 +10,18 @@ class ILDAPService(Interface): def get_user(username: str) -> IUser: """Retrieve the user with the given username.""" - def save_user(user: IUser) -> IUser: + def add_user(user: IUser) -> IUser: """ - Save the user in the database. + Add the user to the database. A new UID and GID will be generated and returned in the new user. """ def get_group(cn: str, is_club: bool = False) -> IGroup: """Retrieve the group with the given cn (Unix group name).""" - def save_group(group: IGroup) -> IGroup: + def add_group(group: IGroup) -> IGroup: """ - Save the group in the database. + Add the group to the database. The GID will not be changed and must be valid. """ @@ -30,3 +30,6 @@ class ILDAPService(Interface): def modify_group(old_group: IGroup, new_group: IGroup): """Replace old_group with new_group.""" + + def add_sudo_role(uid: str): + """Create a sudo role for the club with this UID.""" diff --git a/ceo_common/interfaces/IMailService.py b/ceo_common/interfaces/IMailService.py new file mode 100644 index 000000000..6be18b251 --- /dev/null +++ b/ceo_common/interfaces/IMailService.py @@ -0,0 +1,15 @@ +from typing import Dict + +from zope.interface import Interface + +from .IUser import IUser + + +class IMailService(Interface): + """An interface to send email messages.""" + + def send(_from: str, to: str, headers: Dict[str, str], content: str): + """Send a message with the given headers and content.""" + + def send_welcome_message_to(user: IUser): + """Send a welcome message to the new member.""" diff --git a/ceo_common/interfaces/IMailmanService.py b/ceo_common/interfaces/IMailmanService.py new file mode 100644 index 000000000..94a7ecf46 --- /dev/null +++ b/ceo_common/interfaces/IMailmanService.py @@ -0,0 +1,11 @@ +from zope.interface import Interface + + +class IMailmanService(Interface): + """A service to susbcribe and unsubscribe people from mailing lists.""" + + def subscribe(address: str, mailing_list: str): + """Subscribe the email address to the mailing list.""" + + def unsubscribe(address: str, mailing_list: str): + """Unsubscribe the email address from the mailing list.""" diff --git a/ceo_common/interfaces/IUWLDAPService.py b/ceo_common/interfaces/IUWLDAPService.py new file mode 100644 index 000000000..72c66e808 --- /dev/null +++ b/ceo_common/interfaces/IUWLDAPService.py @@ -0,0 +1,15 @@ +from typing import Union + +from zope.interface import Interface + + +class IUWLDAPService(Interface): + """Represents the UW LDAP database.""" + + def get(username: str): + """ + Return the LDAP record for the given user, or + None if no such record exists. + + :rtype: Union[UWLDAPRecord, None] + """ diff --git a/ceo_common/interfaces/IUser.py b/ceo_common/interfaces/IUser.py index 238c33082..223ee3ce9 100644 --- a/ceo_common/interfaces/IUser.py +++ b/ceo_common/interfaces/IUser.py @@ -24,6 +24,12 @@ class IUser(Interface): # Non-LDAP attributes forwarding_addresses = Attribute('list of email forwarding addresses') + def get_forwarding_addresses(self) -> List[str]: + """Get the forwarding addresses for this user.""" + + def set_forwarding_addresses(self, addresses: List[str]): + """Set the forwarding addresses for this user.""" + def is_club() -> bool: """ Returns True if this is the Unix user for a club. @@ -31,7 +37,10 @@ class IUser(Interface): """ def add_to_ldap(): - """Add a new record to LDAP for this user.""" + """ + Add a new record to LDAP for this user. + A new UID number and GID number will be created. + """ def add_to_kerberos(password: str): """Add a new Kerberos principal for this user.""" @@ -51,14 +60,17 @@ class IUser(Interface): def change_password(password: str): """Replace the user's password.""" - def serialize_for_modlist() -> Dict: + def create_home_dir(): + """Create a new home directory for this user.""" + + def serialize_for_modlist() -> Dict[str, List[bytes]]: """ Serialize this user into a dict to be passed to ldap.modlist.addModlist(). """ # static method - def deserialize_from_dict(data: Dict): + def deserialize_from_dict(data: Dict[str, List[bytes]]): """Deserialize this user from a dict returned by ldap.search_s(). :returns: IUser diff --git a/ceo_common/interfaces/__init__.py b/ceo_common/interfaces/__init__.py index 98243da85..e0d96bec9 100644 --- a/ceo_common/interfaces/__init__.py +++ b/ceo_common/interfaces/__init__.py @@ -3,3 +3,7 @@ from .IConfig import IConfig from .IUser import IUser from .ILDAPService import ILDAPService from .IGroup import IGroup +from .IFileService import IFileService +from .IUWLDAPService import IUWLDAPService +from .IMailService import IMailService +from .IMailmanService import IMailmanService diff --git a/ceo_common/model/Config.py b/ceo_common/model/Config.py index 0993408be..8ba7bd7c4 100644 --- a/ceo_common/model/Config.py +++ b/ceo_common/model/Config.py @@ -9,15 +9,28 @@ class Config: _domain = 'csclub.internal' _ldap_base = ','.join(['dc=' + dc for dc in _domain.split('.')]) _config = { + 'base_domain': _domain, 'ldap_admin_principal': 'ceod/admin', 'ldap_server_url': 'ldap://ldap-master.' + _domain, 'ldap_users_base': 'ou=People,' + _ldap_base, 'ldap_groups_base': 'ou=Group,' + _ldap_base, + 'ldap_sudo_base': 'ou=SUDOers,' + _ldap_base, 'ldap_sasl_realm': _domain.upper(), + 'uwldap_server_url': 'ldap://uwldap.uwaterloo.ca', + 'uwldap_base': 'dc=uwaterloo,dc=ca', 'member_min_id': 20001, 'member_max_id': 29999, 'club_min_id': 30001, 'club_max_id': 39999, + 'member_home': '/users', + 'club_home': '/users', + 'member_home_skel': '/users/skel', + 'club_home_skel': '/users/skel', + 'smtp_url': 'smtp://mail.' + _domain, + 'smtp_starttls': False, + 'mailman3_api_base_url': 'http://localhost:8001/3.1', + 'mailman3_api_username': 'restadmin', + 'mailman3_api_password': 'mailman3', } def get(self, key: str) -> str: diff --git a/ceod/model/FileService.py b/ceod/model/FileService.py new file mode 100644 index 000000000..753dc862e --- /dev/null +++ b/ceod/model/FileService.py @@ -0,0 +1,79 @@ +import os +import pwd +import shutil +from typing import List + +from zope import component +from zope.interface import implementer + +from .validators import is_valid_forwarding_address, InvalidForwardingAddressException +from ceo_common.interfaces import IFileService, IConfig + + +@implementer(IFileService) +class FileService: + def __init__(self): + cfg = component.getUtility(IConfig) + self.member_home_skel = cfg.get('member_home_skel') + self.club_home_skel = cfg.get('club_home_skel') + + def create_home_dir(self, username: str, is_club: bool = False): + if is_club: + skel_dir = self.club_home_skel + else: + skel_dir = self.member_home_skel + pwnam = pwd.getpwnam(username) + home = pwnam.pw_dir + uid = pwnam.pw_uid + gid = pwnam.pw_gid + # recursively copy skel dir to user's home + shutil.copytree(skel_dir, home) + # Set ownership and permissions on user's home. + # The setgid bit ensures that all files created under that + # directory belong to the owner (useful for clubs). + os.chmod(home, mode=0o2751) # rwxr-s--x + os.chown(home, uid=uid, gid=gid) + # recursively set file ownership + for root, dirs, files in os.walk(home): + for dir in dirs: + os.chown(os.path.join(root, dir), uid=uid, gid=gid) + for file in files: + os.chown(os.path.join(root, file), uid=uid, gid=gid) + + def get_forwarding_addresses(self, username: str) -> List[str]: + pwnam = pwd.getpwnam(username) + home = pwnam.pw_dir + forward_file = os.path.join(home, '.forward') + if not os.path.isfile(forward_file): + return [] + lines = [ + line.strip() for line in open(forward_file).readlines() + ] + return [ + line for line in lines + if line != '' and line[0] != '#' + ] + + def set_forwarding_addresses(self, username: str, addresses: List[str]): + for line in addresses: + if not is_valid_forwarding_address(line): + raise InvalidForwardingAddressException(line) + pwnam = pwd.getpwnam(username) + home = pwnam.pw_dir + uid = pwnam.pw_uid + gid = pwnam.pw_gid + forward_file = os.path.join(home, '.forward') + + if os.path.exists(forward_file): + # create a backup + backup_forward_file = forward_file + '.bak' + shutil.copyfile(forward_file, backup_forward_file) + os.chown(backup_forward_file, uid=uid, gid=gid) + else: + # create a new ~/.forward file + open(forward_file, 'w') + os.chown(forward_file, uid=uid, gid=gid) + + with open(forward_file, 'w') as f: + for line in addresses: + f.write(line + '\n') diff --git a/ceod/model/Group.py b/ceod/model/Group.py index 421691ef2..e21761816 100644 --- a/ceod/model/Group.py +++ b/ceod/model/Group.py @@ -40,7 +40,7 @@ class Group: def add_to_ldap(self): self.ldap_srv.add_group(self) - def serialize_for_modlist(self) -> Dict: + def serialize_for_modlist(self) -> Dict[str, List[bytes]]: data = { 'cn': [self.cn], 'gidNumber': [str(self.gid_number)], @@ -55,7 +55,7 @@ class Group: return strings_to_bytes(data) @staticmethod - def deserialize_from_dict(data: Dict) -> IGroup: + def deserialize_from_dict(data: Dict[str, List[bytes]]) -> IGroup: data = bytes_to_strings(data) return Group( cn=data['cn'][0], diff --git a/ceod/model/LDAPService.py b/ceod/model/LDAPService.py index ffdd6eb30..1d11cd23b 100644 --- a/ceod/model/LDAPService.py +++ b/ceod/model/LDAPService.py @@ -10,6 +10,7 @@ from zope.interface import implementer from ceo_common.interfaces import ILDAPService, IKerberosService, IConfig, IUser, IGroup from .User import User from .Group import Group +from .SudoRole import SudoRole class UserNotFoundError: @@ -34,6 +35,7 @@ class LDAPService: self.club_max_id = cfg.get('club_max_id') def _get_ldap_conn(self, gssapi_bind: bool = True) -> ldap.ldapobject.LDAPObject: + # TODO: cache the connection conn = ldap.initialize(self.ldap_server_url) if gssapi_bind: self._gssapi_bind(conn) @@ -99,7 +101,13 @@ class LDAPService: return uid raise Exception('no UIDs remaining') - def save_user(self, user: IUser) -> IUser: + def add_sudo_role(self, uid: str): + conn = self._get_ldap_conn() + sudo_role = SudoRole(uid) + modlist = ldap.modlist.addModlist(sudo_role.serialize_for_modlist()) + conn.add_s(sudo_role.dn, modlist) + + def add_user(self, user: IUser) -> IUser: if user.is_club(): min_id, max_id = self.club_min_id, self.club_max_id else: @@ -112,9 +120,10 @@ class LDAPService: modlist = ldap.modlist.addModlist(new_user.serialize_for_modlist()) conn.add_s(new_user.dn, modlist) + return new_user - def save_group(self, group: IGroup) -> IGroup: + def add_group(self, group: IGroup) -> IGroup: conn = self._get_ldap_conn() # make sure that the caller initialized the GID number assert group.gid_number diff --git a/ceod/model/MailService.py b/ceod/model/MailService.py new file mode 100644 index 000000000..ecb1e6ace --- /dev/null +++ b/ceod/model/MailService.py @@ -0,0 +1,63 @@ +import datetime +from email.message import EmailMessage +import re +import smtplib +from typing import Dict + +import jinja2 +from zope import component +from zope.interface import implementer + +from ceo_common.interfaces import IMailService, IConfig, IUser + +smtp_url_re = re.compile(r'^(?Psmtps?)://(?P[\w.-]+)(:(?P\d+))?$') + + +@implementer(IMailService) +class MailService: + def __init__(self): + cfg = component.getUtility(IConfig) + smtp_url = cfg.get('smtp_url') + match = smtp_url_re.match(smtp_url) + if match is None: + raise Exception('Invalid SMTP URL: %s' % smtp_url) + self.smtps = match.group('scheme') == 'smtps' + self.host = match.group('host') + self.port = int(match.group('port') or 25) + self.starttls = cfg.get('smtp_starttls') + assert not (self.smtps and self.starttls) + self.base_domain = cfg.get('base_domain') + self.jinja_env = jinja2.Environment( + loader=jinja2.PackageLoader('ceod.model'), + ) + + def send(self, _from: str, to: str, headers: Dict[str, str], content: str): + msg = EmailMessage() + msg.set_content(content) + msg['From'] = _from + msg['To'] = to + msg['Date'] = datetime.datetime.now().astimezone().strftime('%a, %d %b %Y %H:%M:%S %z') + for key, val in headers.items(): + msg[key] = val + + if self.smtps: + client = smtplib.SMTP_SSL(self.host, self.port) + else: + client = smtplib.SMTP(self.host, self.port) + client.ehlo() + if self.starttls: + client.starttls() + client.send_message(msg) + client.quit() + + def send_welcome_message_to(self, user: IUser): + template = self.jinja_env.get_template('welcome_message.j2') + # TODO: store surname and givenName in LDAP + first_name = user.cn.split(' ', 1)[0] + body = template.render(name=first_name, user=user.uid) + self.send( + f'Computer Science Club ', + f'{user.cn} <{user.uid}@{self.base_domain}>', + {'Subject': 'Welcome to the Computer Science Club'}, + body, + ) diff --git a/ceod/model/MailmanService.py b/ceod/model/MailmanService.py new file mode 100644 index 000000000..2535baa42 --- /dev/null +++ b/ceod/model/MailmanService.py @@ -0,0 +1,51 @@ +import requests +from requests.auth import HTTPBasicAuth +from zope import component +from zope.interface import implementer + +from ceo_common.interfaces import IMailmanService, IConfig + + +@implementer(IMailmanService) +class MailmanService: + def __init__(self): + cfg = component.getUtility(IConfig) + self.base_domain = cfg.get('base_domain') + self.api_base_url = cfg.get('mailman3_api_base_url') + self.api_username = cfg.get('mailman3_api_username') + self.api_password = cfg.get('mailman3_api_password') + + def subscribe(self, address: str, mailing_list: str): + if '@' in mailing_list: + mailing_list = mailing_list[:mailing_list.index('@')] + if '@' not in address: + address = f'{address}@{self.base_domain}' + url = f'{self.api_base_url}/members' + resp = requests.post( + url, + data={ + 'list_id': f'{mailing_list}.{self.base_domain}', + 'subscriber': address, + 'pre_verified': 'True', + 'pre_confirmed': 'True', + 'pre_approved': 'True', + }, + auth=HTTPBasicAuth(self.api_username, self.api_password), + ) + resp.raise_for_status() + + def unsubscribe(self, address: str, mailing_list: str): + if '@' not in mailing_list: + mailing_list = f'{mailing_list}@{self.base_domain}' + if '@' not in address: + address = f'{address}@{self.base_domain}' + url = f'{self.api_base_url}/lists/{mailing_list}/member/{address}' + resp = requests.delete( + url, + data={ + 'pre_approved': 'True', + 'pre_confirmed': 'True', + }, + auth=HTTPBasicAuth(self.api_username, self.api_password), + ) + resp.raise_for_status() diff --git a/ceod/model/SudoRole.py b/ceod/model/SudoRole.py new file mode 100644 index 000000000..623b79782 --- /dev/null +++ b/ceod/model/SudoRole.py @@ -0,0 +1,31 @@ +from zope import component + +from .utils import strings_to_bytes +from ceo_common.interfaces import IConfig + + +class SudoRole: + """Represents a sudoRole record in LDAP.""" + + def __init__(self, uid: str): + cfg = component.getUtility(IConfig) + ldap_sudo_base = cfg.get('ldap_sudo_base') + + self.uid = uid + self.dn = f'cn=%{uid},{ldap_sudo_base}' + + def serialize_for_modlist(self): + # TODO: use sudoOrder + data = { + 'objectClass': [ + 'top', + 'sudoRole', + ], + 'cn': '%' + self.uid, + 'sudoUser': '%' + self.uid, + 'sudoHost': 'ALL', + 'sudoCommand': 'ALL', + 'sudoOption': '!authenticate', + 'sudoRunAsUser': self.uid, + } + return strings_to_bytes(data) diff --git a/ceod/model/UWLDAPRecord.py b/ceod/model/UWLDAPRecord.py new file mode 100644 index 000000000..a5134799b --- /dev/null +++ b/ceod/model/UWLDAPRecord.py @@ -0,0 +1,30 @@ +from typing import List, Dict, Union + +from .utils import bytes_to_strings + + +class UWLDAPRecord: + """Represents a record from the UW LDAP.""" + + def __init__( + self, + uid: str, + program: Union[str, None], + mail_local_addresses: List[str], + ): + self.uid = uid + self.program = program + self.mail_local_addresses = mail_local_addresses + + @staticmethod + def deserialize_from_dict(self, data: Dict[str, List[bytes]]): + """ + Deserializes a dict returned from ldap.search_s() into a + UWLDAPRecord. + """ + data = bytes_to_strings(data) + return UWLDAPRecord( + uid=data['uid'][0], + program=data.get('ou', [None])[0], + mail_local_addresses=data['mailLocalAddress'], + ) diff --git a/ceod/model/UWLDAPService.py b/ceod/model/UWLDAPService.py new file mode 100644 index 000000000..146f7545b --- /dev/null +++ b/ceod/model/UWLDAPService.py @@ -0,0 +1,23 @@ +from typing import Union + +import ldap +from zope import component +from zope.interface import implementer + +from .UWLDAPRecord import UWLDAPRecord +from ceo_common.interfaces import IUWLDAPService, IConfig + + +@implementer(IUWLDAPService) +class UWLDAPService: + def __init__(self): + cfg = component.getUtility(IConfig) + self.uwldap_server_url = cfg.get('uwldap_server_url') + self.uwldap_base = cfg.get('uwldap_base') + + def get(self, username: str) -> Union[UWLDAPRecord, None]: + conn = ldap.initialize(self.uwldap_server_url) + results = conn.search_s(self.uwldap_base, ldap.SCOPE_SUBTREE, f'uid={username}') + if not results: + return None + return UWLDAPRecord.deserialize_from_dict(results[0]) diff --git a/ceod/model/User.py b/ceod/model/User.py index 6b0573215..d62f412b2 100644 --- a/ceod/model/User.py +++ b/ceod/model/User.py @@ -6,7 +6,8 @@ from zope import component from zope.interface import implementer from .utils import strings_to_bytes, bytes_to_strings -from ceo_common.interfaces import ILDAPService, IKerberosService, IUser, IConfig +from ceo_common.interfaces import ILDAPService, IKerberosService, IFileService, \ + IUser, IConfig @implementer(IUser) @@ -26,6 +27,8 @@ class User: ): if not is_club and not terms and not non_member_terms: raise Exception('terms and non_member_terms cannot both be empty') + cfg = component.getUtility(IConfig) + self.uid = uid self.cn = cn self.program = program @@ -34,20 +37,23 @@ class User: self.login_shell = login_shell self.uid_number = uid_number self.gid_number = gid_number - self.home_directory = home_directory or os.path.join('/users', uid) + if home_directory is None: + if is_club: + home_parent = cfg.get('member_home') + else: + home_parent = cfg.get('club_home') + self.home_directory = os.path.join(home_parent, uid) + else: + self.home_directory = home_directory self.positions = positions or [] self.mail_local_addresses = mail_local_addresses or [] self._is_club = is_club - cfg = component.getUtility(IConfig) self.ldap_sasl_realm = cfg.get('ldap_sasl_realm') self.dn = f'uid={uid},{cfg.get("ldap_users_base")}' self.ldap_srv = component.getUtility(ILDAPService) self.krb_srv = component.getUtility(IKerberosService) - - @property - def forwarding_addresses(self): - raise NotImplementedError() + self.file_srv = component.getUtility(IFileService) def __repr__(self) -> str: lines = [ @@ -80,7 +86,7 @@ class User: return self._is_club def add_to_ldap(self): - new_member = self.ldap_srv.save_user(self) + new_member = self.ldap_srv.add_user(self) self.uid_number = new_member.uid_number self.gid_number = new_member.gid_number @@ -90,6 +96,9 @@ class User: def change_password(self, password: str): self.krb_srv.change_password(self.uid, password) + def create_home_dir(self): + self.file_srv.create_home_dir(self.uid, self._is_club) + def serialize_for_modlist(self) -> Dict: data = { 'cn': [self.cn], @@ -124,7 +133,7 @@ class User: return strings_to_bytes(data) @staticmethod - def deserialize_from_dict(data: Dict) -> IUser: + def deserialize_from_dict(data: Dict[str, List[bytes]]) -> IUser: data = bytes_to_strings(data) return User( uid=data['uid'][0], @@ -167,3 +176,11 @@ class User: new_user.positions.remove(position) self.ldap_srv.modify_user(self, new_user) self.positions = new_user.positions + + def get_forwarding_addresses(self) -> List[str]: + return self.file_srv.get_forwarding_addresses(self.uid) + + def set_forwarding_addresses(self, addresses: List[str]): + self.file_srv.set_forwarding_addresses(self.uid, addresses) + + forwarding_addresses = property(get_forwarding_addresses, set_forwarding_addresses) diff --git a/ceod/model/__init__.py b/ceod/model/__init__.py index 6d09fe5c0..65572dc1f 100644 --- a/ceod/model/__init__.py +++ b/ceod/model/__init__.py @@ -2,3 +2,9 @@ from .KerberosService import KerberosService from .LDAPService import LDAPService, UserNotFoundError, GroupNotFoundError from .User import User from .Group import Group +from .UWLDAPService import UWLDAPService +from .UWLDAPRecord import UWLDAPRecord +from .FileService import FileService +from .SudoRole import SudoRole +from .MailService import MailService +from .MailmanService import MailmanService diff --git a/etc/spam/new-member.d/welcome b/ceod/model/templates/welcome_message.j2 old mode 100755 new mode 100644 similarity index 74% rename from etc/spam/new-member.d/welcome rename to ceod/model/templates/welcome_message.j2 index 38b04aade..b396494b8 --- a/etc/spam/new-member.d/welcome +++ b/ceod/model/templates/welcome_message.j2 @@ -1,25 +1,4 @@ -#!/bin/bash -p - -# This is a privileged script. -IFS=$' \t\n' -PATH=/usr/bin:/bin -unset ENV BASH_ENV CDPATH -umask 077 - -prog=$CEO_PROG -auth=$CEO_AUTH - -tmp="$(tempfile)" -trap "rm $tmp" 0 -exec >"$tmp" - -h_from="Computer Science Club " -h_to="$CEO_NAME <$CEO_USER@csclub.uwaterloo.ca>" -subj="Welcome to the Computer Science Club" - -if test "$prog" = addmember; then - user="$CEO_USER" name="$CEO_NAME" - body="Hello $name: +Hello {{ name }}: Welcome to the Computer Science Club! We are pleased that you have chosen to join us. We welcome you to come out to our events, or just hang out in our office (MC 3036/3037). You have been automatically subscribed to our mailing list, csc-general, which we use to keep you informed of upcoming events. @@ -40,17 +19,17 @@ You can hear about upcoming events in a number of ways: Even when events aren't being held, you are welcome to hang out in the club office (MC 3036/3037, across the hall from MathSoc). It's often open late into the evening, and sells pop and snacks at reasonable prices. If you're so inclined, you are also welcome in our IRC channel, #csc on FreeNode. -You now have a CSC user account with username \"$user\" and the password you supplied when you joined. You can use this account to log into almost any CSC system, including our office terminals and servers. A complete list is available at: +You now have a CSC user account with username "{{ user }}" and the password you supplied when you joined. You can use this account to log into almost any CSC system, including our office terminals and servers. A complete list is available at: http://wiki.csclub.uwaterloo.ca/Machine_List You can connect remotely using SSH. On Windows, PuTTY is a popular SSH client; on Unix-like operating systems, you can connect with the 'ssh' command, like this: - ssh $user@corn-syrup.csclub.uwaterloo.ca + ssh {{ user }}@corn-syrup.csclub.uwaterloo.ca To use CSC web hosting, simply place files in the 'www' directory in your home directory. Files placed there will be available at: - http://csclub.uwaterloo.ca/~$user/ + http://csclub.uwaterloo.ca/~{{ user }}/ We support many server-side technologies, including PHP, Perl and Python. If you need a MySQL database, you can create one for yourself using the 'ceo' command-line tool. @@ -70,18 +49,3 @@ To contact the executive, email: Regards, Computer Science Club Executive -" -elif [[ "$prog" = addclubrep || "$prog" = addclub ]]; then - exit 0 -else - exit 1 -fi - -echo "From: $h_from" -echo "To: $h_to" -echo "Subject: $subj" -echo -echo "$body" | fmt -s - -exec >&2 -env - /usr/sbin/sendmail -t -f "exec@csclub.uwaterloo.ca" < "$tmp" diff --git a/ceod/model/validators.py b/ceod/model/validators.py new file mode 100644 index 000000000..3a53517e1 --- /dev/null +++ b/ceod/model/validators.py @@ -0,0 +1,52 @@ +import inspect +import re +from typing import Callable + + +class InvalidUsernameException(Exception): + pass + + +class InvalidForwardingAddressException(Exception): + pass + + +valid_username_re = re.compile(r'^[a-z][\w-]+$') + +# Only allow usernames and email addresses to be set in ~/.forward +valid_forwarding_address_re = re.compile(r'^[\w-]+|[\w.+-]+@[\w-]+(\.[\w-]+)*$') + + +def is_valid_username(username: str) -> bool: + """Returns True if the username has a valid format.""" + return valid_username_re.match(username) is not None + + +def check_valid_username(f: Callable) -> Callable: + """ + A decorator which raises an Exception if the username passed + to f is invalid. + f must accept `username` as a regular argument. + """ + argspec = inspect.getfullargspec(f) + try: + arg_idx = argspec.args.index('username') + except ValueError: + return f + + def wrapper(*args, **kwargs): + username = None + if arg_idx < len(args): + username = args[arg_idx] + elif 'username' in kwargs: + username = kwargs['username'] + if username is not None and not is_valid_username(username): + raise InvalidUsernameException(username) + return f(*args, **kwargs) + + return wrapper + + +def is_valid_forwarding_address(address: str) -> bool: + """Returns True if the address is a valid forwarding address.""" + return valid_forwarding_address_re.match(address) is not None diff --git a/debian/.gitignore b/debian/.gitignore deleted file mode 100644 index 6ba0e6d9b..000000000 --- a/debian/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -/ceo.substvars -/ceo-common -/ceo-clients -/ceo-daemon -/ceo-python -/files -/*.debhelper -/*.debhelper.log -/*.substvars diff --git a/debian/ceo-clients.manpages b/debian/ceo-clients.manpages deleted file mode 100644 index 58109bcc5..000000000 --- a/debian/ceo-clients.manpages +++ /dev/null @@ -1,2 +0,0 @@ -docs/addclub.1 -docs/addmember.1 diff --git a/debian/ceo-common.dirs b/debian/ceo-common.dirs deleted file mode 100644 index 41eb93b43..000000000 --- a/debian/ceo-common.dirs +++ /dev/null @@ -1 +0,0 @@ -etc/csc diff --git a/debian/ceo-common.install b/debian/ceo-common.install deleted file mode 100644 index 26340e9ec..000000000 --- a/debian/ceo-common.install +++ /dev/null @@ -1 +0,0 @@ -etc/*.cf etc/ops etc/spam etc/csc diff --git a/debian/ceo-daemon.ceod.init b/debian/ceo-daemon.ceod.init deleted file mode 100755 index 59f5fc858..000000000 --- a/debian/ceo-daemon.ceod.init +++ /dev/null @@ -1,55 +0,0 @@ -#! /bin/sh - -### BEGIN INIT INFO -# Provides: ceod -# Required-Start: $remote_fs $syslog $network -# Required-Stop: $remote_fs $syslog $network -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: CEO Daemon -### END INIT INFO - -set -e - -test -x /usr/sbin/ceod || exit 0 - -. /lib/lsb/init-functions - -case "$1" in - start) - log_daemon_msg "Starting CEO Daemon" "ceod" - if start-stop-daemon --start --quiet --oknodo --pidfile /var/run/ceod.pid --exec /usr/sbin/ceod -- -dq; then - log_end_msg 0 - else - log_end_msg 1 - fi - ;; - stop) - log_daemon_msg "Stopping CEO Daemon" "ceod" - if start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/ceod.pid; then - log_end_msg 0 - else - log_end_msg 1 - fi - ;; - - restart|force-reload) - log_daemon_msg "Restarting CEO Daemon" "ceod" - start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile /var/run/ceod.pid - if start-stop-daemon --start --quiet --oknodo --pidfile /var/run/ceod.pid --exec /usr/sbin/ceod -- -dq; then - log_end_msg 0 - else - log_end_msg 1 - fi - ;; - - status) - status_of_proc -p /var/run/ceod.pid /usr/sbin/ceod ceod && exit 0 || exit $? - ;; - - *) - log_action_msg "Usage: /etc/init.d/ceod {start|stop|force-reload|restart|status}" - exit 1 -esac - -exit 0 diff --git a/debian/ceo-daemon.dirs b/debian/ceo-daemon.dirs deleted file mode 100644 index 46d4416a8..000000000 --- a/debian/ceo-daemon.dirs +++ /dev/null @@ -1 +0,0 @@ -etc/ldap/schema diff --git a/debian/ceo-daemon.install b/debian/ceo-daemon.install deleted file mode 100644 index 106cad97b..000000000 --- a/debian/ceo-daemon.install +++ /dev/null @@ -1 +0,0 @@ -etc/csc.schema etc/ldap/schema diff --git a/debian/ceo-daemon.manpages b/debian/ceo-daemon.manpages deleted file mode 100644 index 3f671f5c9..000000000 --- a/debian/ceo-daemon.manpages +++ /dev/null @@ -1 +0,0 @@ -docs/ceod.8 diff --git a/debian/ceo-python.manpages b/debian/ceo-python.manpages deleted file mode 100644 index cc4400f1a..000000000 --- a/debian/ceo-python.manpages +++ /dev/null @@ -1 +0,0 @@ -docs/ceo.1 diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index 9a079563c..000000000 --- a/debian/changelog +++ /dev/null @@ -1,739 +0,0 @@ -ceo (0.7.1-buster1) buster; urgency=medium - - * Update mailman path to use virtualenv - - -- Max Erenberg Tue, 18 May 2021 01:45:49 -0400 - -ceo (0.7.0-buster1) buster; urgency=medium - - * Set userPassword field in LDAP for SASL authentication - - -- Zachary Seguin Fri, 07 May 2021 21:44:02 -0400 - -ceo (0.6.0-buster1.2) buster; urgency=medium - - * Decrease minimum username length from 3 to 2 - - -- Max Erenberg Sun, 02 May 2021 18:02:31 -0400 - -ceo (0.6.0-buster1.1) buster; urgency=medium - - * Use Mailman 3 instead of Mailman 2 - - -- Max Erenberg Sun, 11 Apr 2021 21:54:06 -0400 - -ceo (0.6.0-stretch1) stretch; urgency=high - - * Move adduser and mail operations to phosphoric-acid due to decommissioning - of aspartame - - * Packaging for stretch - - -- Zachary Seguin Sun, 21 Mar 2021 23:04:05 -0400 - -ceo (0.6.0-buster1) buster; urgency=high - - * Move adduser and mail operations to phosphoric-acid due to decommissioning - of aspartame - - -- Zachary Seguin Sun, 21 Mar 2021 22:39:05 -0400 - -ceo (0.5.28-bionic1.1) bionic; urgency=medium - - * Packaging for bionic - - -- Jennifer Zhou Sun, 21 Oct 2018 21:38:57 -0400 - -ceo (0.5.28-buster1) buster; urgency=medium - - * Package for buster - - -- Zachary Seguin Sun, 15 Apr 2018 14:31:08 -0400 - -ceo (0.5.28-xenial1) xenial; urgency=medium - - * Build for xenial - - -- Zachary Seguin Tue, 02 May 2017 00:24:45 -0400 - -ceo (0.5.28-jessie1) jessie; urgency=medium - - * Build for jessie - - -- Zachary Seguin Tue, 02 May 2017 00:16:31 -0400 - -ceo (0.5.28-stretch1) stretch; urgency=medium - - * Check for host (IPv4 or IPV6) or MX record when verying valid email - addresses - - -- Zachary Seguin Wed, 01 May 2017 13:07:21 -0500 - -ceo (0.5.27-stretch1) stretch; urgency=medium - - * Build for stretch - - -- Zachary Seguin Wed, 11 Jan 2017 16:07:21 -0500 - -ceo (0.5.27jessie2) jessie; urgency=low - - * Include library as a dependency - - -- Zachary Seguin Sat, 20 Feb 2016 15:54:29 -0500 - -ceo (0.5.27trusty2) trusty; urgency=medium - - * Include library as a dependency - - -- Zachary Seguin Sat, 20 Feb 2016 15:57:18 -0500 - -ceo (0.5.27trusty1) trusty; urgency=high - - * Resolved issue from previous release which resulted in CEO not launching - - -- Zachary Seguin Fri, 19 Feb 2016 23:38:41 -0500 - -ceo (0.5.27jessie1) jessie; urgency=high - - * Resolved issue from previous release which resulted in CEO not launching - - -- Zachary Seguin Fri, 19 Feb 2016 23:38:41 -0500 - -ceo (0.5.27jessie) jessie; urgency=medium - - * "Library" now launches "librarian" - - -- Felix Bauckholt Fri, 19 Feb 2016 22:12:25 -0500 - -ceo (0.5.26trusty) trusty; urgency=medium - - * "Library" now launches "librarian" - - -- Felix Bauckholt Fri, 19 Feb 2016 22:07:37 -0500 - -ceo (0.5.26) jessie; urgency=medium - - * Repackage for jessie - * Fix build for latest package versions - - -- Zachary Seguin Wed, 11 Nov 2015 22:39:49 -0500 - -ceo (0.5.25jessie0) jessie; urgency=low - - * Replace mention of the safe with the cup. - * Remind users that club accounts are free. - - -- Sean Hunt Tue, 22 Jul 2014 14:20:16 -0400 - -ceo (0.5.24ubuntu5) saucy; urgency=low - - * Packaging for saucy. - - -- Sean Hunt Thu, 05 Dec 2013 15:59:17 -0500 - -ceo (0.5.24jessie0) jessie; urgency=low - - * Packaging for jessie. - - -- Luqman Aden Thu, 10 Oct 2013 21:51:26 -0400 - -ceo (0.5.24squeeze0) oldstable; urgency=low - - * Rebuild for squeeze, since a wheezy package was accepted there by accident. - - -- Jeremy Roman Mon, 16 Sep 2013 08:33:58 -0400 - -ceo (0.5.24) stable; urgency=low - - * Fix bug introduced in Kerberos change. - - -- Jeremy Roman Mon, 16 Sep 2013 08:28:51 -0400 - -ceo (0.5.23) stable; urgency=low - - * Stable is now wheezy; rebuild. - - -- Jeremy Roman Sat, 07 Sep 2013 11:59:24 -0400 - -ceo (0.5.22) stable; urgency=low - - * Drop support for Kerberos LDAP backend; this is not the current CSC setup. - - -- Jeremy Roman Sat, 07 Sep 2013 11:45:33 -0400 - -ceo (0.5.21) testing; urgency=low - - * Build with older protoc-c for compatibility with squeeze. - - -- Marc Burns Tue, 28 May 2013 11:14:36 -0400 - -ceo (0.5.20) testing; urgency=low - - * Work around bug in libgssapi 2.0.25 present in wheezy. - - -- Marc Burns Tue, 28 May 2013 10:45:09 -0400 - -ceo (0.5.19ubuntu2) quantal; urgency=low - - * Packaging for quantal. - - -- Owen Michael Smith Sat, 25 May 2013 19:46:52 -0400 - -ceo (0.5.19ubuntu1) precise; urgency=low - - * Added precise package with changes - - -- Sarah Harvey Wed, 06 Feb 2013 23:44:18 -0500 - -ceo (0.5.19) stable; urgency=low - - * Updated mail, adduser host to be aspartame, not ginseng (following filesystem migration) - - -- Sarah Harvey Wed, 06 Feb 2013 23:36:46 -0500 - -ceo (0.5.18ubuntu1) precise; urgency=low - - * Added precise package with changes. - - -- Sarah Harvey Wed, 12 Sep 2012 08:42:02 -0400 - -ceo (0.5.18) stable; urgency=low - - * Updated mailman host to be mail, not caffeine (following mail container migration) - - -- Sarah Harvey Mon, 10 Sep 2012 19:06:16 -0400 - -ceo (0.5.17ubuntu2) precise; urgency=low - - * Accidentally merged in broken changes. Fixing. - - -- Jeremy Roman Thu, 26 Apr 2012 15:19:03 -0400 - -ceo (0.5.17) stable; urgency=low - - * Change behavior of ceod to add Kerberos principal, - * as opposed to changing principal password. - - -- Marc Burns Fri, 16 Mar 2012 15:27:35 -0400 - -ceo (0.5.16) stable; urgency=low - - * Fix CEO for CMC by allow mailman to be disabled. - - -- Michael Spang Sat, 17 Sep 2011 16:36:01 -0400 - -ceo (0.5.14) stable; urgency=low - - * Add support for sending a welcome message. - - -- Jeremy Roman Fri, 26 Aug 2011 00:59:08 -0400 - -ceo (0.5.13) stable; urgency=low - - * Fix Mailman path - - -- Jeremy Roman Mon, 09 May 2011 19:12:09 -0400 - -ceo (0.5.12) stable; urgency=low - - * Change sudoRunAs to sudoRunAsUser. - - -- Michael Spang Sun, 13 Mar 2011 03:24:30 -0400 - -ceo (0.5.11) stable; urgency=low - - * Fix library check in and search bug introduced in 0.5.9+nmu1. - - -- Marc Burns Fri, 04 Mar 2011 16:52:32 -0500 - -ceo (0.5.10) stable; urgency=low - - * Fix squeeze build warnings - * Add m4burns to debian/control - - -- Michael Spang Fri, 04 Mar 2011 00:47:09 -0500 - -ceo (0.5.9+nmu1) stable; urgency=low - - * Non-maintainer upload. - * Fix library book search page to display message when no books are found. - - -- Marc Burns Mon, 28 Feb 2011 13:00:24 -0500 - -ceo (0.5.9) stable; urgency=low - - * Fix build for squeeze. - - -- Michael Spang Thu, 14 Oct 2010 14:22:04 -0400 - -ceo (0.5.8+nmu1) stable; urgency=low - - * fixed bug reported by jdonland - - -- Jeremy Roman Sun, 26 Sep 2010 22:32:50 -0400 - -ceo (0.5.8) stable; urgency=low - - * tab support in most forms (note that the tab key is already bound for the LDAP lookup fields) - * new members can be added for multiple terms without going through renewal - * fix for the squeeze version of urwid - * new members are automatically added to csc-general - - -- Jeremy Roman Sat, 25 Sep 2010 01:04:02 -0400 - -ceo (0.5.7+nmu4) stable; urgency=low - - * Non-maintainer upload. - * add Office Manager position to positions list - - -- Jeremy Roman Tue, 14 Sep 2010 18:19:50 -0400 - -ceo (0.5.7+nmu3) stable; urgency=low - - * Added phpmyadmin to mysql info file generated by CEO - - -- Michael Ellis Thu, 19 Aug 2010 14:06:16 -0400 - -ceo (0.5.7+nmu2) stable; urgency=low - - * Removed the need for separate entries to manage office and syscom - * Added check to ensure group is valid - - -- Michael Ellis Fri, 18 Jun 2010 21:29:48 -0400 - -ceo (0.5.7+nmu1) stable; urgency=low - - * Non-maintainer upload. - * Removed uwdir lookup for expired accounts emailing - - -- Michael Ellis Tue, 18 May 2010 18:18:02 -0400 - -ceo (0.5.7) stable; urgency=low - - [ Michael Spang ] - * Fix expiredaccounts - - [ Michael Ellis ] - * Reworded expired account email. Club rep accounts can be renewed for - free (as usual). - - [ Michael Spang ] - * Readd quota support - - -- Michael Spang Sun, 09 May 2010 02:10:48 -0400 - -ceo (0.5.6) stable; urgency=low - - [ Michael Spang ] - * Fix use of freopen - * Fix auth for mysql database creation - - [ Jeremy Brandon Roman ] - * added ability to use first letter of menu items - - [ Michael Spang ] - * Remove ternary operators - - -- Michael Spang Sun, 20 Dec 2009 13:45:48 -0500 - -ceo (0.5.5) stable; urgency=low - - * Add missing dependency on python-mysql - * Add CLI version of mysql thing - - -- Michael Spang Mon, 02 Nov 2009 20:34:52 +0000 - -ceo (0.5.4) stable; urgency=low - - * Switch from SCTP to TCP - - -- Michael Spang Mon, 02 Nov 2009 03:04:52 +0000 - -ceo (0.5.3) stable; urgency=low - - * Fix gss error reporting bug - * Clarify email forwarding upon renewal - * Fail fast if not authenticated - * Encrypt all post-auth ceoc<->ceod communication - * Improve error handling when writing - - -- Michael Spang Sat, 24 Oct 2009 14:49:51 -0400 - -ceo (0.5.2) stable; urgency=low - - * Clarify search operation in menu - * Move some code - * Fix segfault - * Write mysql file to ~club - * Kill mathsoclist - * Blacklist orphaned/expired from updateprograms - * Add status thing - * Force redraw after status thing - - -- Michael Spang Wed, 16 Sep 2009 18:32:56 -0400 - -ceo (0.5.1) stable; urgency=low - - * Add mysql magic. - * Add email forwarding magic. - * Labels on the menu. - - -- Michael Spang Wed, 09 Sep 2009 17:54:49 -0400 - -ceo (0.5.0) stable; urgency=low - - * Add ceo daemon. - - -- Michael Spang Thu, 30 Jul 2009 00:19:42 -0400 - -ceo (0.4.24) stable; urgency=low - - * Bump standards version. - - -- Michael Spang Wed, 29 Jul 2009 07:31:24 -0400 - -ceo (0.4.23) stable; urgency=low - - * CEO library now only finds books that are signed out as being overdue. - - -- Michael Gregson Wed, 11 Mar 2009 03:30:01 -0500 - -ceo (0.4.22) stable; urgency=low - - * CEO now closes window when it should. (Sorry) - - -- Michael Gregson Wed, 11 Mar 2009 02:25:01 -0500 - -ceo (0.4.21) stable; urgency=low - - * CEO Library can now add boox. - - -- Michael Gregson Wed, 11 Mar 2009 02:09:01 -0500 - -ceo (0.4.20) stable; urgency=low - - * Update kadmin headers - - -- David Bartley Tue, 24 Feb 2009 16:08:12 -0500 - -ceo (0.4.19) stable; urgency=low - - * Rebuild for lenny. - - -- Michael Spang Tue, 17 Feb 2009 22:23:30 -0500 - -ceo (0.4.18) stable; urgency=low - - [ Michael Gregson ] - * Added new search function, and books now display due dates. - - -- Michael Gregson Wed, 29 Jan 2009 01:04:00 -0500 - -ceo (0.4.17) stable; urgency=low - - [ Michael Gregson ] - * Books can now be returned!!! Yay! - - -- Michael Gregson Thu, 15 Jan 2009 23:42:00 -0500 - -ceo (0.4.16) stable; urgency=low - - [ Michael Gregson ] - * Fixed error in calling of members.current - - -- Michael Gregson Thu, 15 Jan 2009 22:40:00 -0500 - -ceo (0.4.15) stable; urgency=low - - [ Michael Gregson ] - * Fixed incorrect usage of members.registered in library - - -- Michael Gregson Thu, 15 Jan 2009 19:10:00 -0500 - -ceo (0.4.14) stable; urgency=low - - [ Michael Gregson ] - * Corrected members.registered() to account for - non-existent members. - * Corrected overdue search. - - -- Michael Gregson Thu, 15 Jan 2009 18:40:00 -0500 - -ceo (0.4.13) stable; urgency=low - - [ Michael Gregson ] - * Add user validation to library system - * Add search function to library - * Can search for overdue books. - - -- Michael Gregson Thu, 15 Jan 2009 17:00:00 -0500 - -ceo (0.4.12) stable; urgency=low - - [ Michael Gregson ] - * Rewrite library system. - * Support for book checkout and return on sqlobject backends - * We dont die when not having LDAP to connect to. - - -- Michael Gregson Wed, 14 Jan 2009 19:38:00 -0400 - -ceo (0.4.11) stable; urgency=low - - [ David Bartley ] - * Add library path to config - - [ Nick Guenther ] - * library backend, initial version - * Library GUI is coming, but awkwardsadface - * CEO notifies of it's connect attempt (since if LDAP is being sad - then CEO hangs without any indication of why) - * Search works whoooo - * We've gone from not having a library, to having a basic library that - almost works! There's kinks and the code could be cleaner in places, - but it's a really decent start for only a day's work. yayyyy python - - -- David Bartley Mon, 02 Jun 2008 23:49:09 -0400 - -ceo (0.4.10) stable; urgency=low - - [ David Bartley ] - * Always call deauth - * Add configurable refquota support - - [ Michael Spang ] - * Auth as ceo/admin for zfsaddhomedir - - -- David Bartley Wed, 28 May 2008 02:01:53 -0400 - -ceo (0.4.9) stable; urgency=low - - * Move mathsoc regex and exception userid's into config - * Import sys - * Fix help text - * Use refquota instead of quota - - -- David Bartley Thu, 15 May 2008 22:14:50 -0400 - -ceo (0.4.8) stable; urgency=low - - * No point in recommending quota anymore - * Add help for command-line ceo - * Drop memberUid support; all groups use uniqueMember now - * Simplify help - * Improve help message - * Add mathsoclist command - * Add term argument to mathsoclist - - -- David Bartley Thu, 24 Apr 2008 19:57:12 -0400 - -ceo (0.4.7) stable; urgency=low - - [ David Bartley ] - * Add zfsaddhomedir - - [ Michael Spang ] - * Initialize program name in openlog - * Whitespace fix - - -- David Bartley Tue, 25 Mar 2008 14:13:36 -0400 - -ceo (0.4.6) stable; urgency=low - - * Fix off-by-one error - * Search menu bug fix - - -- David Bartley Sat, 15 Mar 2008 02:13:25 -0400 - -ceo (0.4.5) stable; urgency=low - - * Don't offer to update to an empty program - * It's doubtful that a user would need to mount a floppy disk - * Add library stubs and refactor menu creation - * Add inactive command - - -- David Bartley Mon, 10 Mar 2008 00:35:09 -0400 - -ceo (0.4.4) stable; urgency=low - - [ David Bartley ] - * Added console app - * Install ceo.console - * Set params=[] by default in ldapi.search - * Add list_all and uid2dn; make list_* return {dn:...} instead of - {uid:...} - * Implement updateprogram (interactively updates program from uwldap) - * Sort memberlist - * Add office staff to floppy group - * Refactor uwldap constants - * Implement expired account emails - * Add expired-account and notify-hook to git - * Send to both uwdir email and csclub email - * Fix bug in group management - * Refactor console code - - [ Michael Spang ] - * Fix magic - * Fix magic, really - * Actually do magic, tested this time - * Fix use of club settings in addmember - * Fix use of member UID range in addclub - - -- Michael Spang Fri, 25 Jan 2008 20:36:42 -0500 - -ceo (0.4.3) stable; urgency=low - - * Add cro to positions - * Fix typo - * Fix group modification code - - -- David Bartley Tue, 08 Jan 2008 19:58:19 -0500 - -ceo (0.4.2) stable; urgency=low - - [ David Bartley ] - * Add password prompt - * Only allow 3 password attempts - * Remove extraneous whitespace - * Add tab completion for userid fields - * Clarify group failure - * Improve exception handling - * Improved tab-completion - * Add sudo entry to ldap when creating clubs - - [ Michael Spang ] - * Reorganize build process - * Reorganize namespace - * Use python-ldap directly in members - * Cleanup warnings: unused imports, etc - * Better error handling in the gui - * Fix list by term and list by name - * Display "Connecting..." during gui startup - * Remove chfn and chsh and allow shell changes in the gui - * Enlarge the shells list - * Don't try to install chsh and chfn - * Remove python-pam dependency - * Remove ceoquery - * Add manpages and remove TODO - * Allow init of MemberException with no arguments - * Remove obsolete function ceo_add_club() - * POSIX ACL support in addhomedir and addclub - * Add club representative support - * Show "Rep Terms" when displaying member - * Conditionally shows terms - * Add git-buildpackage configuration - - -- Michael Spang Mon, 24 Dec 2007 13:41:27 -0500 - -ceo (0.4.1) stable; urgency=low - - * Minor fixes - - -- Michael Spang Wed, 12 Dec 2007 03:40:17 -0500 - -ceo (0.4.0) stable; urgency=low - - * New release - - -- Michael Spang Wed, 12 Dec 2007 03:07:05 -0500 - -ceo (0.3.9) stable; urgency=low - - * New release - - -- Michael Spang Mon, 10 Dec 2007 03:56:06 -0500 - -ceo (0.3.3) stable; urgency=low - - * Add club and group modify page - * Add sasl support - * Complete group and position management - * Remove ceo-old - * Fix bugs - - -- David Bartley Wed, 21 Nov 2007 20:56:14 -0500 - -ceo (0.3.2) unstable; urgency=low - - [ Michael Spang ] - * Fix CEO group add for rfc2307bis - - [ David Bartley ] - * Add 'search by group' - * Lookup name and program based on uwdir id - * Add group and position management - - -- Michael Spang Wed, 21 Nov 2007 17:21:40 -0500 - -ceo (0.3.1) unstable; urgency=low - - * addhomedir: invalidate nscd tables - * ceo-urwid: add create club account menuitem - * Add urwid to dependencies - - -- Michael Spang Fri, 5 Oct 2007 10:16:41 -0400 - -ceo (0.3.0) unstable; urgency=low - - * Add experimental urwid-based GUI - * Rip out studentid support - * Unbreak termusers in ceoquery - * Increase widths of UI windows - * PgSQL to LDAP transition - - -- Michael Spang Tue, 25 Sep 2007 04:00:10 -0400 - -ceo (0.2.4) unstable; urgency=low - - * Added csc.schema. - * Vim-style keybindings for CEO menus. - * Bug fix: call setreuid(euid, euid) in csc-chfn and csc-chsh. - * Bug fix: run less in "secure" mode. - * Renamed package to ceo. - - -- Michael Spang Mon, 28 May 2007 02:05:28 -0400 - -csc (0.2.3) unstable; urgency=low - - * Added "ceoquery", a utility to retrieve lists of members and users. - * Added "csc-chsh" and "csc-chfn" utilities. - * Bug fix: build_gecos() did not include enough commas between fields. - * Member attributes are now added to LDAP as well as the PgSQL database. - - -- Michael Spang Sun, 18 Feb 2007 21:35:28 -0500 - -csc (0.2.2) unstable; urgency=low - - * Added "addhomedir", a utility to create home directories for new users. - * Bug fix: CEO still referenced an exception that changed name in 0.2. - * Documentation updates. - - -- Michael Spang Mon, 29 Jan 2007 01:47:31 -0500 - -csc (0.2.1) unstable; urgency=low - - * Documentation updates only - * Added docs/GIT-HOWTO and docs/INSTALLING - - -- Michael Spang Sun, 28 Jan 2007 01:24:37 -0500 - -csc (0.2) unstable; urgency=low - - * Tests added to most Python modules. - * Split configuration files. - * Added maintainer scripts to manage permissions during install and purge. - * Added functions for use by tools planned for next release (chfn, etc). - * Added support for account "repair", which will recreate LDAP entries - and principals if necessary. - * The recreate account menu option in ceo is now active. - * Replaced instances of "== None" and "!= None" with "is None" and - "is not None", respectively (thanks to: Nick Guenther). - * Renamed terms.valid() to terms.validate() (thanks to: Nick Guenther). - - -- Michael Spang Fri, 26 Jan 2007 20:10:14 -0500 - -csc (0.1) unstable; urgency=low - - * Initial Release. - - -- Michael Spang Thu, 28 Dec 2006 04:07:03 -0500 - diff --git a/debian/compat b/debian/compat deleted file mode 100644 index 7ed6ff82d..000000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -5 diff --git a/debian/control b/debian/control deleted file mode 100644 index ef7da207c..000000000 --- a/debian/control +++ /dev/null @@ -1,42 +0,0 @@ -Source: ceo -Section: admin -Priority: optional -Maintainer: Systems Committee -Uploaders: Michael Spang , - David Bartley , - Michael Gregson , - Jeremy Roman , - Marc Burns , - Zachary Seguin -Build-Depends: debhelper (>= 5.0.0), python-dev (>= 2.4), dh-python, python, libkrb5-dev (>= 1.7), libldap2-dev, libsasl2-dev, libsctp-dev, libprotobuf-c-dev, libacl1-dev, protobuf-compiler, protobuf-c-compiler -Standards-Version: 3.9.1 - -Package: ceo-common -Architecture: all -Depends: ${misc:Depends} -Description: Computer Science Club Common Files - This package contains the CSC Electronic Office - common files. - -Package: ceo-python -Architecture: all -Replaces: ceo-gui -Conflicts: ceo-gui -Depends: ceo-clients, library, python-ldap, python-urwid, python-sqlobject, python-protobuf, python-psycopg | python-psycopg2, python-mysqldb, python-dnspython, ${python:Depends}, ${shlibs:Depends}, ${misc: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}, ${misc:Depends} -Description: Computer Science Club Administrative Clients - This package contains the CSC Electronic Office - client programs. - -Package: ceo-daemon -Architecture: any -Depends: ceo-python, ${python:Depends}, ${shlibs:Depends}, ${misc:Depends} -Description: Computer Science Club Administrative Daemon - This package contains the CSC Electronic Office - daemon. diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index 76f0cb70f..000000000 --- a/debian/copyright +++ /dev/null @@ -1,31 +0,0 @@ -This package was debianized by Michael Spang on -Thu, 28 Dec 2006 04:07:03 -0500. - -Copyright (c) 2006-2007, Michael Spang -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -* Neither the name of the University of Waterloo Computer Science Club - nor the names of its contributors may be used to endorse or promote - products derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/debian/rules b/debian/rules deleted file mode 100755 index f3b9a484f..000000000 --- a/debian/rules +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/make -f - -CFLAGS := -g -O2 -fstack-protector-all -fPIE -LDFLAGS := -pie -Wl,--as-needed - -build: - cd src && make CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" - -clean: - dh_testdir - dh_testroot - dh_clean - $(MAKE) -C src clean - python setup.py -q clean -a --build-base=build-ceo - python setupd.py -q clean -a --build-base=build-ceod - rm -rf build-ceo build-ceod - -install: build - dh_testdir - dh_testroot - dh_installdirs - python setup.py -q build --build-base=build-ceo install --no-compile -O0 --prefix=/usr --root=debian/ceo-python - python setupd.py -q build --build-base=build-ceod install --no-compile -O0 --prefix=/usr --root=debian/ceo-daemon \ - --install-scripts=/usr/lib/ceod - $(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_installdocs - dh_installexamples - dh_installinit --name ceod -- start 95 2 3 4 5 . stop 05 0 1 6 . - dh_install - dh_installman - dh_link - dh_strip - dh_compress - dh_fixperms - dh_python2 - dh_installdeb - dh_shlibdeps - dh_gencontrol - dh_md5sums - dh_builddeb - -binary-indep: - -binary: binary-indep binary-arch - -.PHONY: build clean binary-indep binary-arch binary install diff --git a/docs/GIT-HOWTO b/docs/GIT-HOWTO deleted file mode 100644 index 44cd62918..000000000 --- a/docs/GIT-HOWTO +++ /dev/null @@ -1,55 +0,0 @@ -Getting the Source ------------------- - -The sources for this project are in a git repository. Git is a distributed -revision control tool originally created by Linus Torvalds to track the Linux -kernel tree. With git, there is generally no central repository that everyone -commits their changes to. Instead, collaboration is done by "pulling" changes -from the repositories of other contributors. - -When you check out the sources, you will get the entire history along with -the latest version. You do not need any special permissions to clone a -repository and start making changes. - -To retrieve the ceo sources, clone the public repository: - - git clone /users/git/public/pyceo.git - -Making Changes --------------- - -Now that you have your own repository, you can start making changes. You -may can add, update, or delete files as necessary and then commit these -changes to your local repository. Then you can make these changes available -to others. Read the documentation to learn more about basic git usage. - -Git Resources -------------- - -For a tutorial, see [1] generally and [2] if you are familiar with CVS. -The manpages for git are also invaluable, use `man git-foo` to view them, -or look online at [3]. - -Finally, if you're interested in how git works internally, see [4] for -documentation of the "core" commands, and [5] for documentation of the -repository format. - -[1] http://www.kernel.org/pub/software/scm/git/docs/tutorial.html -[2] http://www.kernel.org/pub/software/scm/git/docs/cvs-migration.html -[3] http://www.kernel.org/pub/software/scm/git/docs/ -[4] http://www.kernel.org/pub/software/scm/git/docs/core-tutorial.html -[5] http://www.kernel.org/pub/software/scm/git/docs/repository-layout.html - - -Setting up a Public Repository ------------------------------- - -If you make changes, you will probably want to share them with the other -contributors. The only thing other people need to fetch your changes into -their own repository is the location of your repository and read access to -it. With that they can use `git pull` to fetch and merge your changes. - -If you want to make changes but not publish them immediately after each -commit, create a second "public" repository and use "git push" when you -are ready to make your changes public. Refer to the Internet for more -details. diff --git a/docs/INSTALLING b/docs/INSTALLING deleted file mode 100644 index 049f7a2e8..000000000 --- a/docs/INSTALLING +++ /dev/null @@ -1,78 +0,0 @@ - - BUILDING AND INSTALLING - ----------------------- - -This document describes the steps needed to get the package built and -installed on CSC systems. If you don't have authority to do this, you -can safely skip it. - -Building the Package --------------------- - -To build a Debian package out of the sources, run `debuild` at the top -of the source tree. If all goes well, a Debian package and source tarball -will appear in the parent directory. - -Do NOT build the package as root (rather, don't build anything as root in -general). Use 'fakeroot' so that the permissions in the .deb can be set -correctly. It is only necessary to build as root if you are using pbuilder, -which builds in a chroot. - -You can examine the package with tools like dpkg-deb(1) and debdiff(1). -One useful command is `dpkg-deb -c `. This will give you a list -of files that will be installed. - -If your build is a development build, you can safely delete it (it will -be overwritten anyway if a subsequent build has the same version number). -Otherwise copy it to a safe place. - - -Installing the Package ----------------------- - -So you have made your changes and have committed them to your repository, your -last test build was successful, and you're ready to install the package. - -To install the package: - - 1. Compare your debian/changelog with the changelog from the currently - installed package. If your changelog has entries missing, find and - merge them with git. This will ensure you do not overwrite others' - changes. - - The changelog on caffeine is in "/usr/share/doc/csc/changelog.gz". - - 2. Describe your changes in debian/changelog - - Run "dch -v new_version" and add bullets to describe all changes - in the new version. Note that this format must be readable by - dpkg-parsechangelog. - - 4. Commit the changelog update to your repository - - You might want to mention that you are installing the package - (i.e. it's a "release") in the commit message. - - 5. Build the package - - Use 'debuild' to build the package. - - 5. Install the package - - Run `dpkg -i csc__.deb`. - - 6. Archive the package file and source - - You will be left with four files: a .deb, a .tar.gz, a .changes, - and a .dsc. Save these to a safe place (preferably in /users/git - so other can find them easily). - - 7. Push to /users/git/public/pyceo.git - - This is a convenient hub for pushing/pulling between contributors. - You must be in the 'git' group to do this - if you're able to install - the package you will certainly be able to add yourself to this group. - -If everyone follows these steps, every installed version will be a -descendant of the previous. Further, since old versions are archived it -will be easy to quickly get ceo working again after a bad update. diff --git a/docs/addclub.1 b/docs/addclub.1 deleted file mode 100644 index 0e518295f..000000000 --- a/docs/addclub.1 +++ /dev/null @@ -1,13 +0,0 @@ -.TH ADDCLUB 1 "December 16, 2007" -.SH NAME -addclub \- add club accounts to the directory -.SH SYNOPSIS -.B addclub -userid clubname -.SH DESCRIPTION -.B Addclub -creates an LDAP entry and home directory for new club accounts. -.SH SEE ALSO -.BR ceo (1). -.SH AUTHOR -Michael Spang diff --git a/docs/addmember.1 b/docs/addmember.1 deleted file mode 100644 index 2765aa413..000000000 --- a/docs/addmember.1 +++ /dev/null @@ -1,16 +0,0 @@ -.TH ADDMEMBER 1 "December 16, 2007" -.SH NAME -addmember \- add club members to the directory -.SH SYNOPSIS -.B addmember -userid name [ program ] -.SH DESCRIPTION -.B Addmember -performs all tasks necessary for creation of a new CSC member. It creates -an LDAP entry, Kerberos principal, and home directory for the new member. -It does NOT register the new member for any terms. This must be done after -the member is created. -.SH SEE ALSO -.BR ceo (1). -.SH AUTHOR -Michael Spang diff --git a/docs/ceo.1 b/docs/ceo.1 deleted file mode 100644 index ebbe59d0e..000000000 --- a/docs/ceo.1 +++ /dev/null @@ -1,17 +0,0 @@ -.TH CEO 1 "December 16, 2007" -.SH NAME -ceo \- CSC Electronic Office -.SH DESCRIPTION -CSC Electronic Office is used to manage membership registration and -user accounts for the Computer Science Club. It has a graphical -user interface, started by typing -.B ceo -with no arguments. -.PP -.SH SEE ALSO -.BR addmember (1), -.BR addclub (1). -.SH AUTHORS -Michael Spang -.br -David Bartley diff --git a/docs/ceod.8 b/docs/ceod.8 deleted file mode 100644 index 373e57e35..000000000 --- a/docs/ceod.8 +++ /dev/null @@ -1,13 +0,0 @@ -.TH CEOD 1 "September 9, 2009" -.SH NAME -ceo \- CSC Electronic Office Daemon -.SH DESCRIPTION -CSC Electronic Office Daemon is used by CEO for operations -that require superuser privileges. -.B ceo -with no arguments. -.PP -.SH SEE ALSO -.BR ceo (1), -.SH AUTHORS -Michael Spang diff --git a/etc/accounts.cf b/etc/accounts.cf deleted file mode 100644 index 26a1286c7..000000000 --- a/etc/accounts.cf +++ /dev/null @@ -1,48 +0,0 @@ -# /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_skel = "/users/skel" - -### Club Account Options ### - -club_min_id = 30001 -club_max_id = 39999 -club_shell = "/bin/bash" -club_home = "/users" -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" diff --git a/etc/csc.schema b/etc/csc.schema deleted file mode 100644 index e10439882..000000000 --- a/etc/csc.schema +++ /dev/null @@ -1,35 +0,0 @@ -# CSC Member Information Schema - -attributetype ( 1.3.6.1.4.1.27934.1.1.1 NAME 'term' - EQUALITY caseIgnoreIA5Match - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{5} ) - -attributetype ( 1.3.6.1.4.1.27934.1.1.2 NAME 'program' - EQUALITY caseIgnoreIA5Match - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{1024} SINGLE-VALUE ) - -attributetype ( 1.3.6.1.4.1.27934.1.1.3 NAME 'studentid' - EQUALITY caseIgnoreIA5Match - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{8} SINGLE-VALUE ) - -attributetype ( 1.3.6.1.4.1.27934.1.1.4 NAME 'position' - EQUALITY caseIgnoreIA5Match - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} ) - -attributetype ( 1.3.6.1.4.1.27934.1.1.5 NAME 'nonMemberTerm' - EQUALITY caseIgnoreIA5Match - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{5} ) - -objectclass ( 1.3.6.1.4.1.27934.1.2.1 NAME 'member' - SUP top AUXILIARY - MUST ( cn $ uid ) - MAY ( studentid $ program $ term $ nonMemberTerm $ description $ position ) ) - -objectclass ( 1.3.6.1.4.1.27934.1.2.2 NAME 'club' - SUP top AUXILIARY - MUST ( cn $ uid ) ) - -objectclass ( 1.3.6.1.4.1.27934.1.2.3 NAME 'group' - SUP top STRUCTURAL - MUST ( cn ) - MAY ( uniqueMember ) ) diff --git a/etc/mailman.cf b/etc/mailman.cf deleted file mode 100644 index e5a54724f..000000000 --- a/etc/mailman.cf +++ /dev/null @@ -1,2 +0,0 @@ -members_list = csc-general -list_domain = csclub.uwaterloo.ca diff --git a/etc/ops/adduser b/etc/ops/adduser deleted file mode 100644 index 2635bf148..000000000 --- a/etc/ops/adduser +++ /dev/null @@ -1 +0,0 @@ -phosphoric-acid adduser root 0x01 diff --git a/etc/ops/mail b/etc/ops/mail deleted file mode 100644 index 3bd132fdd..000000000 --- a/etc/ops/mail +++ /dev/null @@ -1 +0,0 @@ -phosphoric-acid mail root 0x02 diff --git a/etc/ops/mailman b/etc/ops/mailman deleted file mode 100644 index 090186c07..000000000 --- a/etc/ops/mailman +++ /dev/null @@ -1 +0,0 @@ -mail mailman list 0x04 diff --git a/etc/ops/mysql b/etc/ops/mysql deleted file mode 100644 index d6bd10e61..000000000 --- a/etc/ops/mysql +++ /dev/null @@ -1 +0,0 @@ -caffeine mysql mysql 0x03 diff --git a/etc/spam/expired-account b/etc/spam/expired-account deleted file mode 100755 index a0fd96656..000000000 --- a/etc/spam/expired-account +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh - -name=$1 -email=$2 -shift 2 - -tmp="$(tempfile)" -trap "rm $tmp" 0 -exec >"$tmp" - -echo "From: Computer Science Club " -echo "Reply-to: CSClub Exec " -echo "To: $name <$email>" -echo "Subject: [CSClub] Account Expiration" -echo "" -echo "Hello, - -We noticed that your Computer Science Club membership has expired. We would -like to remind you of the many benefits of being a member of the club: - -* 4 GiB of disk quota -* Web space -* Email address -* Shell account -* Access to our library - -If you would like to renew your membership the fee is \$2 per term; club rep -accounts may be renewed for free. You may use one of the following methods to -pay the renewal fee: - -* Come by our office (MC 3036) -* Send us a PayPal donation and send us the transaction id; see - http://csclub.uwaterloo.ca/about/donations for details -* Mail us a cheque; here's our address: - Computer Science Club - Math & Computer 3036/3037 - University of Waterloo - 200 University Avenue West - Waterloo, ON N3L 3G1 - Canada - -If you have any questions, feel free to contact us by phone at -(519) 888-4567 x33870, or by email at exec@csclub.uwaterloo.ca. - -Regards, - -The Computer Science Club" - -exec >&- 2>&- -/usr/sbin/sendmail -t -f "ceo@csclub.uwaterloo.ca" < "$tmp" diff --git a/etc/spam/new-member b/etc/spam/new-member deleted file mode 100755 index f937fcf6f..000000000 --- a/etc/spam/new-member +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -p - -# This is a privileged script. -IFS=$' \t\n' -PATH=/usr/bin:/bin -unset ENV BASH_ENV CDPATH -umask 077 - -cd `dirname $0` - -export CEO_PROG="$1" CEO_AUTH="$2" CEO_USER="$3" CEO_NAME="$4" CEO_DEPT="$5" CEO_STATUS="$6" -export CEO_OUTPUT="$(cat)" -run-parts --umask=077 new-member.d diff --git a/etc/spam/new-member.d/announce b/etc/spam/new-member.d/announce deleted file mode 100755 index aea5cd442..000000000 --- a/etc/spam/new-member.d/announce +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash -p - -# This is a privileged script. -IFS=$' \t\n' -PATH=/usr/bin:/bin -unset ENV BASH_ENV CDPATH -umask 077 - -prog=$CEO_PROG -auth=$CEO_AUTH - -tmp="$(tempfile)" -trap "rm $tmp" 0 -exec >"$tmp" - -authrn="$(getent passwd "$auth" | awk -F: '{ print $5 }' | sed -e 's/,.*//')" - -h_from="$prog " -h_to="Membership and Accounts " -h_cc="$authrn <$auth@csclub.uwaterloo.ca>" - -if [[ "$prog" = addmember || "$prog" == addclubrep ]]; then - user="$CEO_USER" name="$CEO_NAME" dept="$CEO_DEPT" status="$CEO_STATUS" - subj="New Member: $user" - test -z "$dept" && dept="things unknown" - body="Name: $name -Account: $user -Program: $dept -Added by: $auth" - -elif [[ "$prog" = addclub ]]; then - user="$CEO_USER" name="$CEO_NAME" status="$CEO_STATUS" - subj="New Club Account: $user" - body="Club: $name -Account: $user -Added by: $auth" - -else - exit 1 -fi - -output="$CEO_OUTPUT" - -if test "$status" = "failure"; then - subj="$subj (FAILURES)" -fi - -echo "From: $h_from" -echo "To: $h_to" -echo "Cc: $h_cc" -echo "X-Auth-User: $auth" -echo "X-New-User: $user" -echo "X-New-Name: $name" -echo "Subject: $subj" -echo -echo "$body" | fmt -s -echo - -if test "$status" = "success"; then - echo all failures went undetected -elif test -n "$output"; then - echo "$output" -fi - -echo -echo Your Friend, -echo "$prog" - -exec >&2 -env - /usr/sbin/sendmail -t -f "ceo@csclub.uwaterloo.ca" < "$tmp" diff --git a/misc/expired-account b/misc/expired-account deleted file mode 100755 index 9cacb8687..000000000 --- a/misc/expired-account +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh - -name=$1 -email=$2 -shift 2 - -tmp="$(tempfile)" -trap "rm $tmp" 0 -exec >"$tmp" - -echo "From: Computer Science Club " -echo "Reply-to: CSClub Exec " -echo "To: $name <$email>" -echo "Subject: [CSClub] Account Expiration" -echo "" -echo "Hello, - -We noticed that your Computer Science Club membership has expired. We would -like to remind you of the many benifits of being a member of the club: - -* 2 GiB of disk quota -* Web space -* Email address -* Shell account -* Access to our library - -If you would like to renew your membership (the fee is \$2 per term), we have -various methods of doing so: - -* Come by our office (MC 3036) -* Send us a PayPal donation and send us the transaction id; see - http://csclub.uwaterloo.ca/about/donations for details -* Mail us a cheque; here's our address: - Computer Science Club - Math & Computer 3036/3037 - University of Waterloo - 200 University Avenue West - Waterloo, ON N3L 3G1 - Canada - -If you have any questions, feel free to contact us by phone at -(519) 888-4567 x33870, or by email at exec@csclub.uwaterloo.ca. - -Regards, - -The Computer Science Club" - -exec >&- 2>&- -/usr/sbin/sendmail -t -f "ceo@csclub.uwaterloo.ca" < "$tmp" diff --git a/misc/notify-hook b/misc/notify-hook deleted file mode 100755 index a6851b37d..000000000 --- a/misc/notify-hook +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash -p - -# This is a privileged script. -IFS=$' \t\n' -PATH=/usr/bin:/bin -unset ENV BASH_ENV CDPATH -umask 077 - -prog=$1 -auth=$2 -shift 2 - -tmp="$(tempfile)" -trap "rm $tmp" 0 -exec >"$tmp" - -authrn="$(getent passwd "$auth" | awk -F: '{ print $5 }' | sed -e 's/,.*//')" - -h_from="$prog " -h_to="Membership and Accounts " -h_cc="$authrn <$auth@csclub.uwaterloo.ca>" - -if test "$prog" = addmember; then - user=$1 name=$2 dept=$3 status=$4; shift 4 - subj="New Member: $user" - test -z "$dept" && dept="things unknown" - body="Name: $name -Account: $user -Program: $dept -Added by: $auth" - -elif test "$prog" = addclub; then - user=$1 name=$2 status=$4; shift 4 - subj="New Club Account: $user" - body="Club: $name -Account: $user -Added by: $auth" - -else - exit 1 -fi - -output=$(cat) - -if test "$status" = "failure"; then - subj="$subj (FAILURES)" -fi - -echo "From: $h_from" -echo "To: $h_to" -echo "Cc: $h_cc" -echo "X-Auth-User: $auth" -echo "X-New-User: $user" -echo "X-New-Name: $name" -echo "Subject: $subj" -echo -echo "$body" | fmt -s -echo - -if test "$status" = "success"; then - echo all failures went undetected -elif test -n "$output"; then - echo "$output" -fi - -echo -echo Your Friend, -echo "$prog" - -exec >&2 -env - /usr/sbin/sendmail -t -f "ceo@csclub.uwaterloo.ca" < "$tmp" diff --git a/setup.py b/setup.py deleted file mode 100755 index 5a040f22b..000000000 --- a/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -from distutils.core import setup - -setup( - name='ceo', - description='CSC Electronic Office', - packages=[ 'ceo', 'ceo.urwid', 'ceo.console' ], - scripts=['bin/ceo'], -) - diff --git a/setupd.py b/setupd.py deleted file mode 100755 index f120e460d..000000000 --- a/setupd.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python - -from distutils.core import setup - -setup( - name='ceod', - description='CSC Electronic Office Daemon', - scripts=['src/op-mysql','src/op-mailman'], -) - diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index cabaea743..000000000 --- a/src/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -*.o -.*.swp -.nfs* -/addmember -/addclub -/adduser -/op-adduser -/op-mail -/zfsaddhomedir -/config-test -/ceod -/ceoc -/ceo.pb-c.c -/ceo.pb-c.h diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index d95d48ef3..000000000 --- a/src/Makefile +++ /dev/null @@ -1,86 +0,0 @@ -CFLAGS := -g3 -O2 -Wall -Werror -DDEBUG -LDFLAGS := -Wl,--as-needed -INCLUDES := $(shell krb5-config --cflags) -override CFLAGS += -std=gnu99 $(INCLUDES) - -DESTDIR := -PREFIX := /usr/local - -BIN_PROGS := addmember addclub ceod -LIB_PROGS := ceoc op-adduser op-mail -EXT_PROGS := config-test - -LDAP_OBJECTS := ldap.o -LDAP_LIBS := -lldap -LDAP_PROGS := op-adduser -KRB5_OBJECTS := krb5.o kadm.o -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 := $(shell krb5-config --libs gssapi) -NET_PROGS := ceod ceoc -PROTO_OBJECTS := ceo.pb-c.o -PROTO_LIBS := -lprotobuf-c -PROTO_PROGS := op-adduser op-mail addmember addclub -CONFIG_OBJECTS := config.o parser.o -CONFIG_LIBS := -CONFIG_PROGS := $(LDAP_PROGS) $(KRB5_PROGS) $(NET_PROGS) $(PROTO_PROGS) -UTIL_OBJECTS := util.o strbuf.o -UTIL_PROGS := config-test $(CONFIG_PROGS) - -all: $(BIN_PROGS) $(LIB_PROGS) $(EXT_PROGS) ../ceo/ceo_pb2.py - -clean: - rm -f $(BIN_PROGS) $(LIB_PROGS) $(EXT_PROGS) *.o ceo.pb-c.c ceo.pb-c.h - rm -f ceo_pb2.py ../ceo/ceo_pb2.py - -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 - -%: %.o - $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ - -../ceo/ceo_pb2.py: ceo.proto - protoc --python_out=../ceo ceo.proto - -ceod: dmaster.o dslave.o - $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ - -config-test: config-test.o parser.o - -config.o: config.h config-vars.h - -install_clients: - install -d $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(PREFIX)/lib/ceod - install addmember addclub $(DESTDIR)$(PREFIX)/bin - install ceoc $(DESTDIR)$(PREFIX)/lib/ceod - -install_daemon: - install -d $(DESTDIR)$(PREFIX)/sbin $(DESTDIR)$(PREFIX)/lib/ceod - install ceod $(DESTDIR)$(PREFIX)/sbin - install op-adduser $(DESTDIR)$(PREFIX)/lib/ceod - install op-mail $(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): LDLIBS += $(KRB5_LIBS) -$(KRB5_PROGS): $(KRB5_OBJECTS) -$(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): LDLIBS += $(UTIL_LIBS) -$(UTIL_PROGS): $(UTIL_OBJECTS) - -.PHONY: clean all install install_clients install_daemon diff --git a/src/addclub.c b/src/addclub.c deleted file mode 100644 index c843f0d5d..000000000 --- a/src/addclub.c +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "config.h" -#include "ldap.h" -#include "krb5.h" -#include "kadm.h" -#include "ceo.pb-c.h" - -char *prog = NULL; - -static char *name = NULL; -static char *userid = NULL; - -static struct option opts[] = { - { 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(void) { - struct strbuf preq = STRBUF_INIT; - struct strbuf pret = STRBUF_INIT; - char cpath[1024]; - char *cargv[] = { "ceoc", "adduser", NULL }; - int ret = 0; - - if (snprintf(cpath, sizeof(cpath), "%s/ceoc", lib_dir) >= sizeof(cpath)) - fatal("path too long"); - - Ceo__AddUser req; - ceo__add_user__init(&req); - - req.username = userid; - req.realname = name; - req.type = CEO__ADD_USER__TYPE__CLUB; - - strbuf_grow(&preq, ceo__add_user__get_packed_size(&req)); - strbuf_setlen(&preq, ceo__add_user__pack(&req, (uint8_t *)preq.buf)); - - if (spawnvem(cpath, cargv, environ, &preq, &pret, 0)) - return 1; - - Ceo__AddUserResponse *resp = ceo__add_user_response__unpack(NULL, - pret.len, (uint8_t *)pret.buf); - if (!resp) - fatal("failed to unpack response"); - - for (int i = 0; i < resp->n_messages; i++) { - if (resp->messages[i]->status) { - ret = -1; - error("%s", resp->messages[i]->message); - } else { - notice("%s", resp->messages[i]->message); - } - } - - ceo__add_user_response__free_unpacked(resp, NULL); - strbuf_release(&preq); - strbuf_release(&pret); - - return ret; -} - -int main(int argc, char *argv[]) { - int opt; - int ret; - - prog = xstrdup(basename(argv[0])); - init_log(prog, LOG_PID, LOG_AUTHPRIV, 1); - - configure(); - - while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { - switch (opt) { - case '?': - usage(); - break; - default: - fatal("error parsing arguments"); - } - } - - if (argc - optind != 2 && argc - optind != 3) - usage(); - - userid = argv[optind++]; - name = argv[optind++]; - - lib_dir = getenv("CEO_LIB_DIR") ?: default_lib_dir; - - ret = addclub(); - - free_config(); - free(prog); - - return ret; -} diff --git a/src/addmember.c b/src/addmember.c deleted file mode 100644 index 9a1b3b0bf..000000000 --- a/src/addmember.c +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "config.h" -#include "ldap.h" -#include "krb5.h" -#include "kadm.h" -#include "ceo.pb-c.h" - -char *prog = NULL; - -static int use_stdin = 0; - -static char *name = NULL; -static char *userid = NULL; -static char *program = NULL; -static char password[1024]; - -static struct option opts[] = { - { "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(void) { - struct strbuf preq = STRBUF_INIT; - struct strbuf pret = STRBUF_INIT; - char cpath[1024]; - char *cargv[] = { "ceoc", "adduser", NULL }; - int ret = 0; - - 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__AddUser req; - ceo__add_user__init(&req); - - req.username = userid; - req.password = password; - req.program = program; - req.realname = name; - req.type = CEO__ADD_USER__TYPE__MEMBER; - - strbuf_grow(&preq, ceo__add_user__get_packed_size(&req)); - strbuf_setlen(&preq, ceo__add_user__pack(&req, (uint8_t *)preq.buf)); - - if (spawnvem(cpath, cargv, environ, &preq, &pret, 0)) - return 1; - - Ceo__AddUserResponse *resp = ceo__add_user_response__unpack(NULL, - pret.len, (uint8_t *)pret.buf); - if (!resp) - fatal("failed to unpack response"); - - for (int i = 0; i < resp->n_messages; i++) { - if (resp->messages[i]->status) { - ret = -1; - error("%s", resp->messages[i]->message); - } else { - notice("%s", resp->messages[i]->message); - } - } - - ceo__add_user_response__free_unpacked(resp, NULL); - strbuf_release(&preq); - strbuf_release(&pret); - - return ret; -} - -int main(int argc, char *argv[]) { - int opt; - int ret; - - prog = xstrdup(basename(argv[0])); - init_log(prog, LOG_PID, LOG_AUTHPRIV, 1); - - configure(); - - while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { - switch (opt) { - case 's': - use_stdin = 1; - break; - case '?': - usage(); - break; - default: - fatal("error parsing arguments"); - } - } - - if (argc - optind != 2 && argc - optind != 3) - usage(); - - userid = argv[optind++]; - name = argv[optind++]; - - if (argc - optind) - program = argv[optind++]; - - lib_dir = getenv("CEO_LIB_DIR") ?: default_lib_dir; - - ret = addmember(); - - free_config(); - free(prog); - - return ret; -} diff --git a/src/ceo.proto b/src/ceo.proto deleted file mode 100644 index e16f9feaa..000000000 --- a/src/ceo.proto +++ /dev/null @@ -1,43 +0,0 @@ -package ceo; - -message StatusMessage { - required int32 status = 1; - required string message = 2; -} - -message AddUser { - enum Type { - MEMBER = 1; - CLUB = 2; - CLUB_REP = 3; - } - - required Type type = 1; - required string username = 2; - optional string password = 3; - optional string realname = 4; - optional string program = 5; - optional string email = 6; -} - -message AddUserResponse { - repeated StatusMessage messages = 1; -} - -message UpdateMail { - required string username = 1; - optional string forward = 2; -} - -message UpdateMailResponse { - repeated StatusMessage messages = 1; -} - -message AddMySQLUser { - required string username = 1; -} - -message AddMySQLUserResponse { - repeated StatusMessage messages = 1; - optional string password = 2; -} diff --git a/src/ceoc.c b/src/ceoc.c deleted file mode 100644 index c939671d8..000000000 --- a/src/ceoc.c +++ /dev/null @@ -1,167 +0,0 @@ -#include -#include -#include -#include -#include - -#include "util.h" -#include "net.h" -#include "gss.h" -#include "ops.h" -#include "config.h" - -char *prog = NULL; - -static struct option opts[] = { - { NULL, 0, NULL, '\0' }, -}; - -static void usage() { - fprintf(stderr, "Usage: %s op\n", prog); - exit(2); -} - -static void send_gss_token(int sock, struct sockaddr *addr, socklen_t addrlen, gss_buffer_t token) { - OM_uint32 maj_stat, min_stat; - - if (ceo_send_message(sock, token->value, token->length, MSG_AUTH)) - fatalpe("write"); - - 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 strbuf msg = STRBUF_INIT; - uint32_t msgtype; - 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 (ceo_receive_message(sock, &msg, &msgtype)) - fatal("connection closed during auth"); - - if (msgtype != MSG_AUTH) - fatal("unexpected message type 0x%x", msgtype); - - 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_STREAM, IPPROTO_TCP); - struct sockaddr_in addr; - uint32_t msgtype; - struct strbuf in_cipher = STRBUF_INIT, out_cipher = STRBUF_INIT; - - 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; - - if (connect(sock, (struct sockaddr *)&addr, sizeof(addr))) - fatalpe("connect"); - - client_acquire_creds("ceod", hostname); - client_gss_auth(sock, (sa *)&addr, sizeof(addr)); - - gss_encipher(in, &in_cipher); - - if (ceo_send_message(sock, in_cipher.buf, in_cipher.len, op->id)) - fatalpe("write"); - - if (ceo_receive_message(sock, &out_cipher, &msgtype)) - fatal("no response received for op %s", op->name); - - gss_decipher(&out_cipher, out); - - if (msgtype != op->id) - fatal("wrong message type from server: expected %d got %d", op->id, msgtype); - - if (close(sock)) - fatalpe("close"); - - strbuf_release(&in_cipher); - strbuf_release(&out_cipher); -} - -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, LOG_PID, LOG_USER, 1); - - configure(); - setup_ops(); - setup_fqdn(); - - while ((opt = getopt_long(argc, argv, "", opts, NULL)) != -1) { - switch (opt) { - case '?': - usage(); - break; - default: - fatal("error parsing arguments"); - } - } - - if (argc - optind != 1) - usage(); - - op = argv[optind++]; - - ret = client_main(op); - - free_gss(); - free_fqdn(); - free_config(); - free_ops(); - free(prog); - - return ret; -} diff --git a/src/config-test.c b/src/config-test.c deleted file mode 100644 index 5e0f0d9b5..000000000 --- a/src/config-test.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include - -#include "parser.h" -#include "util.h" - -void config_var(const char *name, const char *value) { - printf("%s = \"%s\"\n", name, value); -} - -int main(int argc, char *argv[]) { - if (argc < 2) { - fprintf(stderr, "usage: %s filename\n\n", argv[0]); - exit(1); - } - - config_parse(argv[1]); - - return 0; -} diff --git a/src/config-vars.h b/src/config-vars.h deleted file mode 100644 index 0380dc452..000000000 --- a/src/config-vars.h +++ /dev/null @@ -1,24 +0,0 @@ -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(krb5_realm) -CONFIG_STR(krb5_admin_principal) - -CONFIG_STR(ldap_server_url) -CONFIG_STR(ldap_users_base) -CONFIG_STR(ldap_groups_base) -CONFIG_STR(ldap_sudo_base) -CONFIG_STR(ldap_sasl_mech) -CONFIG_STR(ldap_sasl_realm) -CONFIG_STR(ldap_admin_principal) diff --git a/src/config.c b/src/config.c deleted file mode 100644 index 7dffe9e60..000000000 --- a/src/config.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include - -#include "config.h" -#include "parser.h" -#include "util.h" - -#define DEF_STR NULL -#define DEF_INT LONG_MIN - -#define CONFIG_STR(x) char *x = DEF_STR; -#define CONFIG_INT(x) long x = DEF_INT; -#include "config-vars.h" -#undef CONFIG_STR -#undef CONFIG_INT - -struct config_var { - const char *name; - void *p; - enum { CONFIG_TYPE_STR, CONFIG_TYPE_INT } type; -}; - -#define CONFIG_STR(x) {#x, &x, CONFIG_TYPE_STR }, -#define CONFIG_INT(x) {#x, &x, CONFIG_TYPE_INT }, -static struct config_var config_vars[] = { -#include "config-vars.h" -}; -#undef CONFIG_STR -#undef CONFIG_INT - -const char *default_config_dir = "/etc/csc"; -const char *config_filename = "accounts.cf"; -const char *config_dir; - -void config_var(char *var, char *val) { - int i; - - for (i = 0; i < sizeof(config_vars)/sizeof(*config_vars); i++) { - if (!strcmp(var, config_vars[i].name)) { - switch (config_vars[i].type) { - case CONFIG_TYPE_STR: - if (*(char **)config_vars[i].p) - free(*(char **)config_vars[i].p); - *(char **)config_vars[i].p = xstrdup(val); - break; - case CONFIG_TYPE_INT: - *(long *)config_vars[i].p = config_long(var, val); - break; - default: - fatal("unknown config var type %d", config_vars[i].type); - } - } - } -} - -void configure(void) { - int i; - char conffile[1024]; - - config_dir = getenv("CEO_CONFIG_DIR") ?: default_config_dir; - - if (snprintf(conffile, sizeof(conffile), "%s/%s", config_dir, config_filename) >= sizeof(conffile)) - fatal("huge config path"); - - config_parse(conffile); - - for (i = 0; i < sizeof(config_vars)/sizeof(*config_vars); i++) { - switch (config_vars[i].type) { - case CONFIG_TYPE_STR: - if (*(char **)config_vars[i].p == DEF_STR) - badconf("undefined string variable: %s", config_vars[i].name); - break; - case CONFIG_TYPE_INT: - if (*(long *)config_vars[i].p == DEF_INT) - badconf("undefined integer variable: %s", config_vars[i].name); - break; - default: - fatal("unknown config var type %d", config_vars[i].type); - } - } -} - -void free_config(void) { - for (int i = 0; i < sizeof(config_vars)/sizeof(*config_vars); i++) { - if (config_vars[i].type == CONFIG_TYPE_STR) { - free(*(char **)config_vars[i].p); - *(char **)config_vars[i].p = NULL; - } - } -} diff --git a/src/config.h b/src/config.h deleted file mode 100644 index 57527c53f..000000000 --- a/src/config.h +++ /dev/null @@ -1,10 +0,0 @@ -#define CONFIG_STR(x) extern char *x; -#define CONFIG_INT(x) extern long x; -#include "config-vars.h" -#undef CONFIG_STR -#undef CONFIG_INT - -void configure(void); -void free_config(void); - -extern const char *config_dir; diff --git a/src/daemon.h b/src/daemon.h deleted file mode 100644 index 146a6ba94..000000000 --- a/src/daemon.h +++ /dev/null @@ -1,7 +0,0 @@ -/* dmain.c */ -extern int terminate; -extern int fatal_signal; - -/* dslave.c */ -void slave_main(int sock, struct sockaddr *addr); -void setup_slave(void); diff --git a/src/dmaster.c b/src/dmaster.c deleted file mode 100644 index bc8ea0280..000000000 --- a/src/dmaster.c +++ /dev/null @@ -1,218 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "net.h" -#include "config.h" -#include "gss.h" -#include "daemon.h" -#include "ldap.h" -#include "kadm.h" -#include "krb5.h" -#include "ops.h" - -static struct option opts[] = { - { "detach", 0, NULL, 'd' }, - { "quiet", 0, NULL, 'q' }, - { 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_pidfile(void) { - int fd; - size_t pidlen; - char pidbuf[1024]; - const char *pidfile = "/var/run/ceod.pid"; - - fd = open(pidfile, O_CREAT|O_RDWR, 0644); - if (fd < 0) - fatalpe("open: %s", pidfile); - if (lockf(fd, F_TLOCK, 0)) - fatalpe("lockf: %s", pidfile); - if (ftruncate(fd, 0)) - fatalpe("ftruncate: %s", pidfile); - pidlen = snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid()); - if (pidlen >= sizeof(pidbuf)) - fatal("pid too long"); - if (full_write(fd, pidbuf, pidlen)) - fatalpe("write: %s", pidfile); -} - -static void setup_daemon(void) { - if (detach) { - if (chdir("/")) - fatalpe("chdir('/')"); - pid_t pid = fork(); - if (pid < 0) - fatalpe("fork"); - if (pid) - exit(0); - if (setsid() < 0) - fatalpe("setsid"); - - setup_pidfile(); - - if (!freopen("/dev/null", "r", stdin)) - fatalpe("freopen"); - if (!freopen("/dev/null", "w", stdout)) - fatalpe("freopen"); - if (!freopen("/dev/null", "w", stderr)) - fatalpe("freopen"); - } -} - -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, opt; - 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_TCP); - if (sock < 0) - fatalpe("socket"); - - opt = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) - fatalpe("setsockopt"); - - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) - fatalpe("bind"); - - if (listen(sock, 128)) - fatalpe("listen"); - - setup_fqdn(); - setup_signals(); - setup_auth(); - setup_ops(); - setup_daemon(); - - 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, 0); - - while ((opt = getopt_long(argc, argv, "dq", opts, NULL)) != -1) { - switch (opt) { - case 'd': - detach = 1; - break; - case 'q': - log_set_maxprio(LOG_WARNING); - break; - case '?': - usage(); - break; - default: - fatal("error parsing arguments"); - } - } - - configure(); - - if (argc != optind) - usage(); - - ret = master_main(); - - free_config(); - free(prog); - - return ret; -} diff --git a/src/dslave.c b/src/dslave.c deleted file mode 100644 index 1a36dc3f7..000000000 --- a/src/dslave.c +++ /dev/null @@ -1,149 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "strbuf.h" -#include "net.h" -#include "config.h" -#include "gss.h" -#include "daemon.h" -#include "ldap.h" -#include "kadm.h" -#include "krb5.h" -#include "ops.h" - -static void signal_handler(int sig) { - if (sig == SIGSEGV) { - error("segmentation fault"); - signal(sig, SIG_DFL); - raise(sig); - } else if (sig != SIGCHLD) { - fatal("unhandled signal %d", sig); - } -} - -static void setup_slave_sigs(void) { - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sigemptyset(&sa.sa_mask); - sa.sa_handler = signal_handler; - - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGSEGV, &sa, NULL); - - signal(SIGINT, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGPIPE, SIG_IGN); - - if (terminate) - raise(fatal_signal); -} - -static void 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); - struct strbuf in_plain = STRBUF_INIT, out_plain = STRBUF_INIT; - char *envp[16]; - - if (!op) - fatal("operation %x does not exist", in_type); - - debug("running op: %s", op->name); - - /* TEMPORARY */ - if (!client_username()) - fatal("unathenticated"); - - gss_decipher(in, &in_plain); - - make_env(envp, "LANG", "C", "CEO_USER", client_username(), - "CEO_CONFIG_DIR", config_dir, NULL); - char *argv[] = { op->path, NULL, }; - - if (spawnvemu(op->path, argv, envp, &in_plain, &out_plain, 0, op->user)) - fatal("child %s failed", op->path); - - gss_encipher(&out_plain, out); - - if (!out->len) - fatal("no response from op"); - - free_env(envp); - strbuf_release(&in_plain); - strbuf_release(&out_plain); -} - -static void handle_one_message(int sock, struct strbuf *in, uint32_t msgtype) { - struct strbuf out = STRBUF_INIT; - - if (msgtype == MSG_AUTH) - handle_auth_message(in, &out); - else - handle_op_message(msgtype, in, &out); - - if (out.len && ceo_send_message(sock, out.buf, out.len, msgtype)) - fatalpe("write"); - - strbuf_release(&out); -} - -void slave_main(int sock, struct sockaddr *addr) { - char addrstr[INET_ADDRSTRLEN]; - struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; - uint32_t msgtype; - 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(); - - while (!terminate) { - if (ceo_receive_message(sock, &msg, &msgtype)) - break; - handle_one_message(sock, &msg, msgtype); - } - - notice("connection closed by peer %s", addrstr); - - strbuf_release(&msg); - - /* stuff allocated by dmaster */ - free_gss(); - free_config(); - free_fqdn(); - free_ops(); - free(prog); -} - diff --git a/src/gss.c b/src/gss.c deleted file mode 100644 index 888567edb..000000000 --- a/src/gss.c +++ /dev/null @@ -1,307 +0,0 @@ -#include -#include -#include -#include - -#include "util.h" -#include "gss.h" -#include "net.h" -#include "strbuf.h" - -static gss_cred_id_t my_creds = GSS_C_NO_CREDENTIAL; -static gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; -static gss_name_t peer_name = GSS_C_NO_NAME; -static gss_name_t imported_service = GSS_C_NO_NAME; -static char *peer_principal; -static char *peer_username; -static OM_uint32 ret_flags; -static int complete; -char service_name[128]; - -void free_gss(void) { - OM_uint32 maj_stat, min_stat; - - if (peer_name) { - maj_stat = gss_release_name(&min_stat, &peer_name); - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_release_name", maj_stat, min_stat); - } - - if (imported_service) { - maj_stat = gss_release_name(&min_stat, &imported_service); - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_release_name", maj_stat, min_stat); - } - - if (context_handle) { - maj_stat = gss_delete_sec_context(&min_stat, &context_handle, GSS_C_NO_BUFFER); - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_delete_sec_context", maj_stat, min_stat); - } - - if (my_creds) { - maj_stat = gss_release_cred(&min_stat, &my_creds); - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_release_creds", maj_stat, min_stat); - } - - free(peer_principal); - free(peer_username); -} - -static char *gssbuf2str(gss_buffer_t buf) { - char *msgstr = xmalloc(buf->length + 1); - memcpy(msgstr, buf->value, buf->length); - msgstr[buf->length] = '\0'; - return msgstr; -} - -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; - char *msgstr; - - maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID, - &msg_ctx, &msg); - (void)maj_stat; - msgstr = gssbuf2str(&msg); - logmsg(LOG_ERR, "%s: %s", prefix, msgstr); - gss_release_buffer(&min_stat, &msg); - free(msgstr); - - while (msg_ctx) { - maj_stat = gss_display_status(&min_stat, code, type, GSS_C_NULL_OID, - &msg_ctx, &msg); - msgstr = gssbuf2str(&msg); - logmsg(LOG_ERR, "additional: %s", msgstr); - gss_release_buffer(&min_stat, &msg); - free(msgstr); - } -} - -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); - - /* Work around bug in libgssapi 2.0.25 / gssapi_krb5 2.2: - * The expiry time returned by gss_acquire_cred is always zero. */ - { - int names_match = 0; - gss_name_t cred_service; - gss_cred_usage_t cred_usage; - maj_stat = gss_inquire_cred(&min_stat, my_creds, &cred_service, &time_rec, &cred_usage, NULL); - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_inquire_cred", maj_stat, min_stat); - - if (time_rec != GSS_C_INDEFINITE) - fatal("credentials valid for %d seconds (oops)", time_rec); - - maj_stat = gss_compare_name(&min_stat, imported_service, cred_service, &names_match); - - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_compare_name", maj_stat, min_stat); - - if (!names_match) - fatal("credentials granted for wrong service (oops)"); - - if (!(cred_usage & GSS_C_ACCEPT)) - fatal("credentials lack usage GSS_C_ACCEPT (oops)"); - } -} - -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) { - if (!complete) - fatal("authentication checked before finishing"); - return peer_principal; -} - -char *client_username(void) { - if (!complete) - fatal("authentication checked before finishing"); - return peer_username; -} - -void gss_encipher(struct strbuf *plain, struct strbuf *cipher) { - OM_uint32 maj_stat, min_stat; - gss_buffer_desc plain_tok, cipher_tok; - int conf_state; - - plain_tok.value = plain->buf; - plain_tok.length = plain->len; - - maj_stat = gss_wrap(&min_stat, context_handle, 1, GSS_C_QOP_DEFAULT, - &plain_tok, &conf_state, &cipher_tok); - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_wrap", maj_stat, min_stat); - - if (!conf_state) - fatal("gss_encipher: confidentiality service required"); - - strbuf_add(cipher, cipher_tok.value, cipher_tok.length); - - maj_stat = gss_release_buffer(&min_stat, &cipher_tok); - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_release_buffer", maj_stat, min_stat); -} - -void gss_decipher(struct strbuf *cipher, struct strbuf *plain) { - OM_uint32 maj_stat, min_stat; - gss_buffer_desc plain_tok, cipher_tok; - int conf_state; - gss_qop_t qop_state; - - cipher_tok.value = cipher->buf; - cipher_tok.length = cipher->len; - - maj_stat = gss_unwrap(&min_stat, context_handle, &cipher_tok, - &plain_tok, &conf_state, &qop_state); - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_unwrap", maj_stat, min_stat); - - if (!conf_state) - fatal("gss_encipher: confidentiality service required"); - - strbuf_add(plain, plain_tok.value, plain_tok.length); - - maj_stat = gss_release_buffer(&min_stat, &plain_tok); - if (maj_stat != GSS_S_COMPLETE) - gss_fatal("gss_release_buffer", maj_stat, min_stat); -} - - diff --git a/src/gss.h b/src/gss.h deleted file mode 100644 index a48464789..000000000 --- a/src/gss.h +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -void server_acquire_creds(const char *service); -void client_acquire_creds(const char *service, const char *hostname); -void gss_fatal(char *msg, OM_uint32 maj_stat, OM_uint32 min_stat); -int process_server_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok); -int process_client_token(gss_buffer_t incoming_tok, gss_buffer_t outgoing_tok); -int initial_client_token(gss_buffer_t outgoing_tok); -char *client_principal(void); -char *client_username(void); -void free_gss(void); - -void gss_encipher(struct strbuf *plain, struct strbuf *cipher); -void gss_decipher(struct strbuf *cipher, struct strbuf *plain); diff --git a/src/homedir.c b/src/homedir.c deleted file mode 100644 index 9077be0f8..000000000 --- a/src/homedir.c +++ /dev/null @@ -1,171 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "homedir.h" -#include "util.h" -#include "config.h" - -static int set_acl(char *dir, char *acl_text, acl_type_t type) { - acl_t acl = acl_from_text(acl_text); - if (acl == (acl_t)NULL) { - errorpe("acl_from_text: %s", acl_text); - return -1; - } - if (acl_set_file(dir, type, acl) != 0) { - errorpe("acl_set_file: %s %s 0x%X %p", acl_text, dir, (int)type, (void*)acl); - acl_free(acl); - return -1; - } - acl_free(acl); - - return 0; -} - -int ceo_create_home(char *homedir, char *skel, uid_t uid, gid_t gid, char *access_acl, char *default_acl, char *email) { - int mask; - DIR *skeldir; - struct dirent *skelent; - - mask = umask(0); - - if (mkdir(homedir, 0755)) { - errorpe("failed to create %s", homedir); - return -1; - } - - if (access_acl && set_acl(homedir, access_acl, ACL_TYPE_ACCESS) != 0) - return -1; - if (default_acl && set_acl(homedir, default_acl, ACL_TYPE_DEFAULT) != 0) - return -1; - - 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", skel, 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 (full_write(destfd, buf, bytes)) { - warnpe("write: %s", src); - 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(skeldir); - - if (email && *email) { - char dest[PATH_MAX]; - snprintf(dest, sizeof(dest), "%s/%s", homedir, ".forward"); - int destfd = open(dest, O_WRONLY|O_CREAT|O_EXCL, 0644); - - if (full_write(destfd, email, strlen(email))) - warnpe("write: %s", dest); - - if (fchown(destfd, uid, gid)) - errorpe("chown: %s", dest); - - close(destfd); - } - - if (chown(homedir, uid, gid)) { - errorpe("failed to chown %s", homedir); - return -1; - } - - umask(mask); - - return 0; -} - -int ceo_set_quota(char *proto, int id) { - char user[128]; - char *sqargs[] = { "setquota", "-a", "-p", proto, NULL, NULL }; - - snprintf(user, sizeof(user), "%d", id); - sqargs[4] = user; - - if (spawnv("/usr/sbin/setquota", sqargs)) { - error("failed to set quota for %s", user); - return -1; - } - - return 0; -} diff --git a/src/homedir.h b/src/homedir.h deleted file mode 100644 index fa09eccd1..000000000 --- a/src/homedir.h +++ /dev/null @@ -1,6 +0,0 @@ -#include - -#define CLUB_ACL "u::rwx,g::r-x,o::r-x,g:%d:rwx,m::rwx" - -int ceo_create_home(char *homedir, char *skel, uid_t uid, gid_t gid, char *access_acl, char *default_acl, char *email); -int ceo_set_quota(char *proto, int id); diff --git a/src/kadm.c b/src/kadm.c deleted file mode 100644 index 74b46ac43..000000000 --- a/src/kadm.c +++ /dev/null @@ -1,97 +0,0 @@ -#include - -#include "kadm.h" -#include "krb5.h" -#include "util.h" -#include "config.h" - -extern char *prog; - -static void *handle; - -void ceo_kadm_init() { - krb5_error_code retval; - kadm5_config_params params; - memset((void *) ¶ms, 0, sizeof(params)); - - debug("kadmin: initializing using keytab for %s", krb5_admin_principal); - - retval = kadm5_init_with_skey( -#ifdef KADM5_API_VERSION_3 - context, -#endif - krb5_admin_principal, NULL, - KADM5_ADMIN_SERVICE, ¶ms, KADM5_STRUCT_VERSION, - KADM5_API_VERSION_2, NULL, &handle); - 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; - - debug("kadmin: adding principal %s", user); - - // Added March 2012: Change behavior of ceod to add the kerberos principal. - kadm5_policy_ent_rec defpol; - kadm5_principal_ent_rec princ; - - memset((void*) &princ, 0, sizeof(princ)); - - if ((retval = kadm5_get_policy(handle, "default", &defpol))) { - com_err(prog, retval, "while retrieving default policy"); - return retval; - } - kadm5_free_policy_ent(handle, &defpol); - - princ.policy = "default"; - - if ((retval = krb5_parse_name(context, user, &princ.principal))) { - com_err(prog, retval, "while parsing user name"); - return retval; - } - - long flags = KADM5_POLICY | KADM5_PRINCIPAL; - if ((retval = kadm5_create_principal(handle, &princ, flags, password))) { - if(retval == KADM5_DUP) { - if ((retval = kadm5_chpass_principal(handle, princ.principal, password))) { - com_err(prog, retval, "while setting principal password"); - return retval; - } - } else { - com_err(prog, retval, "while creating principal"); - return retval; - } - } - - krb5_free_principal(context, princ.principal); - return 0; -} - -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; - } - - retval = kadm5_delete_principal(handle, princ); - if (retval && retval != KADM5_UNK_PRINC) { - com_err(prog, retval, "while deleting principal"); - return retval; - } - - krb5_free_principal(context, princ); - return 0; -} diff --git a/src/kadm.h b/src/kadm.h deleted file mode 100644 index b46c41e47..000000000 --- a/src/kadm.h +++ /dev/null @@ -1,5 +0,0 @@ -void ceo_kadm_init(); -void ceo_kadm_cleanup(); - -int ceo_add_princ(char *, char *); -int ceo_del_princ(char *); diff --git a/src/krb5.c b/src/krb5.c deleted file mode 100644 index d04573315..000000000 --- a/src/krb5.c +++ /dev/null @@ -1,132 +0,0 @@ -#include - -#include -#include - -#include "krb5.h" -#include "util.h" -#include "config.h" - -extern char *prog; - -krb5_context context; - -static void com_err_hk(const char *whoami, long code, const char *fmt, va_list args) { - char message[4096]; - char *msgp = message; - - msgp += snprintf(msgp, sizeof(message) - 2 - (msgp - message), "%s ", error_message(code)); - if (msgp - message > sizeof(message) - 2) - fatal("error message overflowed"); - - msgp += vsnprintf(msgp, sizeof(message) - 2 - (msgp - message), fmt, args); - if (msgp - message > sizeof(message) - 2) - fatal("error message overflowed"); - - *msgp++ = '\n'; - *msgp++ = '\0'; - - logmsg(LOG_ERR, "fatal: %s", message); - exit(1); -} - -void ceo_krb5_init() { - krb5_error_code retval; - - 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, krb5_realm); - if (retval) - com_err(prog, retval, "while setting default realm"); -} - -void ceo_krb5_auth(char *principal) { - krb5_error_code retval; - krb5_creds creds; - krb5_principal princ; - 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", principal); - - if ((retval = krb5_cc_default(context, &cache))) - com_err(prog, retval, "while resolving credentials cache"); - - 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))) - com_err(prog, retval, "while initializing credentials cache"); - - if ((retval = krb5_cc_store_cred(context, cache, &creds))) - com_err(prog, retval, "while storing credentials"); - - krb5_free_cred_contents(context, &creds); - krb5_free_principal(context, princ); - krb5_cc_close(context, cache); -} - -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"); - - if ((retval = krb5_cc_destroy(context, cache))) - com_err(prog, retval, "while destroying credentials cache"); -} - -void ceo_krb5_cleanup() { - debug("krb5: cleaning up"); - krb5_free_context(context); -} - -int ceo_read_password(char *password, unsigned int size, int use_stdin) { - int tries = 0; - unsigned int len; - - do { - if (use_stdin) { - if (fgets(password, size, stdin) == NULL) - fatal("eof while reading password"); - - size = strlen(password); - - if (password[size - 1] == '\n') - password[size - 1] = '\0'; - } else { - len = size; - int retval = krb5_read_password(context, "New password", "Confirm password", password, &len); - if (retval == KRB5_LIBOS_PWDINTR) { - error("interrupted"); - return -1; - } else if (retval == KRB5_LIBOS_BADPWDMATCH) { - fputs("Passwords do not match.\n", stderr); - } else if (!password || !*password) { - fputs("Please enter a password.\n", stderr); - } - } - } while (++tries < 3 && !*password); - - if (!*password) { - error("maximum tries exceeded reading password"); - return -1; - } - - return 0; -} diff --git a/src/krb5.h b/src/krb5.h deleted file mode 100644 index 5877b790f..000000000 --- a/src/krb5.h +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -extern char *prog; - -extern krb5_context context; - -void ceo_krb5_init(); -void ceo_krb5_cleanup(); - -void ceo_krb5_auth(char *); -void ceo_krb5_deauth(); - -int ceo_read_password(char *, unsigned int, int); diff --git a/src/ldap.c b/src/ldap.c deleted file mode 100644 index 1d9678a30..000000000 --- a/src/ldap.c +++ /dev/null @@ -1,383 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define LDAP_DEPRECATED 1 -#include - -#include "ldap.h" -#include "krb5.h" -#include "config.h" -#include "util.h" - -extern char *prog; - -LDAP *ld; - -static void ldap_fatal(char *msg) { - int errnum = 0; - char *errstr = NULL; - char *detail = NULL; - - if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &errnum) != LDAP_SUCCESS) - warn("ldap_get_option(LDAP_OPT_ERROR_NUMBER) failed"); - if (ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &detail) != LDAP_SUCCESS) - warn("ldap_get_option(LDAP_OPT_ERROR_STRING) failed"); - - errstr = ldap_err2string(errnum); - - if (detail) - fatal("%s: %s (%d): %s", msg, errstr, errnum, detail); - else if (errnum) - fatal("%s: %s (%d)", msg, errstr, errnum); - else - fatal("%s", msg); -} - -static void ldap_err(char *msg) { - int errnum = 0; - char *errstr = NULL; - char *detail = NULL; - - if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &errnum) != LDAP_SUCCESS) - warn("ldap_get_option(LDAP_OPT_ERROR_NUMBER) failed"); - if (ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &detail) != LDAP_SUCCESS) - warn("ldap_get_option(LDAP_OPT_ERROR_STRING) failed"); - - errstr = ldap_err2string(errnum); - - if (detail) - error("%s: %s (%d): %s", msg, errstr, errnum, detail); - else if (errnum) - error("%s: %s (%d)", msg, errstr, errnum); - else - error("%s", msg); -} - -int ceo_add_group(char *cn, char *basedn, int no) { - if (!cn || !basedn) - fatal("addgroup: Invalid argument"); - - LDAPMod *mods[8]; - int i = -1; - int ret = 0; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "objectClass"; - char *objectClasses[] = { "top", "group", "posixGroup", NULL }; - mods[i]->mod_values = objectClasses; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "cn"; - char *uids[] = { cn, NULL }; - mods[i]->mod_values = uids; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "gidNumber"; - char idno[16]; - snprintf(idno, sizeof(idno), "%d", no); - char *gidNumbers[] = { idno, NULL }; - mods[i]->mod_values = gidNumbers; - - mods[++i] = NULL; - - char dn[1024]; - snprintf(dn, sizeof(dn), "cn=%s,%s", cn, basedn); - - if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) { - ldap_err("addgroup"); - ret = -1; - } - - for (i = 0; mods[i]; i++) - free(mods[i]); - - return ret; -} - -int ceo_add_group_sudo(char *group, char *basedn) { - if (!group || !basedn) - fatal("addgroup: Invalid argument"); - - LDAPMod *mods[8]; - int i = -1; - int ret = 0; - - char cn[17]; - snprintf(cn, sizeof(cn), "%%%s", group); - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "objectClass"; - char *objectClasses[] = { "top", "sudoRole", NULL }; - mods[i]->mod_values = objectClasses; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "cn"; - char *uids[] = { cn, NULL }; - mods[i]->mod_values = uids; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "sudoUser"; - char *sudouser[] = { cn, NULL }; - mods[i]->mod_values = sudouser; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "sudoHost"; - char *sudohost[] = { "ALL", NULL }; - mods[i]->mod_values = sudohost; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "sudoCommand"; - char *sudocommand[] = { "ALL", NULL }; - mods[i]->mod_values = sudocommand; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "sudoOption"; - char *sudooption[] = { "!authenticate", NULL }; - mods[i]->mod_values = sudooption; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "sudoRunAsUser"; - char *sudorunas[] = { group, NULL }; - mods[i]->mod_values = sudorunas; - - char dn[1024]; - snprintf(dn, sizeof(dn), "cn=%%%s,%s", group, basedn); - - mods[++i] = NULL; - - if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) { - ldap_err("addgroup"); - ret = -1; - } - - for (i = 0; mods[i]; i++) - free(mods[i]); - - return ret; -} - -int ceo_add_user(char *uid, char *basedn, char *objclass, char *cn, char *home, char *shell, int no, ...) { - va_list args; - - if (!uid || !basedn || !cn || !home || !shell) - fatal("adduser: Invalid argument"); - - LDAPMod *mods[16]; - char *vals[16][2]; - 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, NULL, NULL }; - if (objclass != NULL) - objectClasses[classes++] = objclass; - mods[i]->mod_values = objectClasses; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "uid"; - char *uids[] = { uid, NULL }; - mods[i]->mod_values = uids; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "cn"; - char *cns[] = { cn, NULL }; - mods[i]->mod_values = cns; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "loginShell"; - char *shells[] = { shell, NULL }; - mods[i]->mod_values = shells; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "uidNumber"; - char idno[16]; - snprintf(idno, sizeof(idno), "%d", no); - char *uidNumbers[] = { idno, NULL }; - mods[i]->mod_values = uidNumbers; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "gidNumber"; - mods[i]->mod_values = uidNumbers; - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = "homeDirectory"; - char *homeDirectory[] = { home, NULL }; - mods[i]->mod_values = homeDirectory; - - va_start(args, no); - char *attr; - while ((attr = va_arg(args, char *))) { - char *val = va_arg(args, char *); - - if (!val || !*val) - continue; - - if (i == sizeof(mods) / sizeof(*mods) - 2) { - error("too many attributes"); - return -1; - } - - mods[++i] = xmalloc(sizeof(LDAPMod)); - mods[i]->mod_op = LDAP_MOD_ADD; - mods[i]->mod_type = attr; - vals[i][0] = val; - vals[i][1] = NULL; - mods[i]->mod_values = vals[i]; - } - - mods[++i] = NULL; - - char dn[1024]; - snprintf(dn, sizeof(dn), "uid=%s,%s", uid, basedn); - - if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) { - ldap_err("adduser"); - ret = -1; - } - - for (i = 0; mods[i]; i++) - free(mods[i]); - - return ret; -} - -int ceo_new_uid(int min, int max) { - char filter[64]; - char *attrs[] = { LDAP_NO_ATTRS, NULL }; - LDAPMessage *res; - int i; - - for (i = min; i <= max; i++) { - // id taken due to passwd - if (getpwuid(i) != NULL) - continue; - - // id taken due to group - if (getgrgid(i) != NULL) - continue; - - snprintf(filter, sizeof(filter), "(|(uidNumber=%d)(gidNumber=%d))", i, i); - if (ldap_search_s(ld, ldap_users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 1, &res) != LDAP_SUCCESS) { - ldap_err("firstuid"); - return -1; - } - - int count = ldap_count_entries(ld, res); - ldap_msgfree(res); - - // id taken due to LDAP - if (count) - continue; - - return i; - } - - return -1; -} - -int ceo_user_exists(char *uid) { - char *attrs[] = { LDAP_NO_ATTRS, NULL }; - LDAPMessage *msg = NULL; - char filter[128]; - int count; - - if (!uid) - fatal("null uid"); - - snprintf(filter, sizeof(filter), "uid=%s", uid); - - if (ldap_search_s(ld, ldap_users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) { - ldap_err("user_exists"); - return -1; - } - - count = ldap_count_entries(ld, msg); - ldap_msgfree(msg); - - return count > 0; -} - -int ceo_group_exists(char *cn) { - char *attrs[] = { LDAP_NO_ATTRS, NULL }; - LDAPMessage *msg = NULL; - char filter[128]; - int count; - - if (!cn) - fatal("null cd"); - - snprintf(filter, sizeof(filter), "cn=%s", cn); - - if (ldap_search_s(ld, ldap_groups_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) { - ldap_err("group_exists"); - return -1; - } - - count = ldap_count_entries(ld, msg); - ldap_msgfree(msg); - - return count > 0; -} - -static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in) { - sasl_interact_t *interact = in; - - while (interact->id != SASL_CB_LIST_END) { - switch (interact->id) { - - // GSSAPI doesn't require any callbacks - - default: - interact->result = ""; - interact->len = 0; - } - - interact++; - } - - return LDAP_SUCCESS; -} - -void ceo_ldap_init() { - int proto = LDAP_DEFAULT_PROTOCOL; - - if (!ldap_admin_principal) - fatal("not configured"); - - 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"); - - if (ldap_sasl_interactive_bind_s(ld, NULL, ldap_sasl_mech, NULL, NULL, - LDAP_SASL_QUIET, &ldap_sasl_interact, NULL) != LDAP_SUCCESS) - ldap_fatal("Bind failed"); -} - -void ceo_ldap_cleanup() { - ldap_unbind(ld); -} diff --git a/src/ldap.h b/src/ldap.h deleted file mode 100644 index b29b68bda..000000000 --- a/src/ldap.h +++ /dev/null @@ -1,12 +0,0 @@ -#define LDAP_DEFAULT_PROTOCOL LDAP_VERSION3 - -int ceo_add_user(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); - -void ceo_ldap_init(); -void ceo_ldap_cleanup(); - -int ceo_user_exists(char *); -int ceo_group_exists(char *); diff --git a/src/net.c b/src/net.c deleted file mode 100644 index a238f1f71..000000000 --- a/src/net.c +++ /dev/null @@ -1,95 +0,0 @@ -#include -#include -#include -#include -#include - -#include "util.h" -#include "net.h" -#include "gss.h" -#include "strbuf.h" - -struct strbuf fqdn = STRBUF_INIT; - -const size_t MAX_MSGLEN = 65536; -const size_t MSG_BUFINC = 4096; - -void setup_fqdn(void) { - struct utsname uts; - struct hostent *lo; - - if (uname(&uts)) - fatalpe("uname"); - lo = gethostbyname(uts.nodename); - if (!lo) - fatalpe("gethostbyname"); - - strbuf_addstr(&fqdn, lo->h_name); -} - -void free_fqdn(void) { - strbuf_release(&fqdn); -} - -int ceo_send_message(int sock, void *buf, size_t len, uint32_t msgtype) { - uint32_t msgheader[2]; - msgheader[0] = htonl(len); - msgheader[1] = htonl(msgtype); - - if (full_write(sock, msgheader, sizeof(msgheader)) < 0) - fatalpe("write"); - - if (full_write(sock, buf, len) < 0) - fatalpe("write"); - - return 0; -} - -int ceo_receive_message(int sock, struct strbuf *msg, uint32_t *msgtype) { - uint32_t msglen, received = 0; - uint32_t msgheader[2]; - ssize_t bytes; - - strbuf_reset(msg); - - while (received < sizeof(msgheader)) { - bytes = read(sock, msgheader, sizeof(msgheader) - received); - if (bytes < 0) { - if (errno == EAGAIN) - continue; - fatalpe("read"); - } - if (!bytes && !received) - return -1; - if (!bytes) - fatalpe("short header received"); - received += bytes; - } - - msglen = ntohl(msgheader[0]); - *msgtype = ntohl(msgheader[1]); - received = 0; - - if (!msglen) - fatal("length is zero in message header"); - - if (msglen > MAX_MSGLEN) - fatal("length is huge in message header"); - - strbuf_grow(msg, msglen); - strbuf_setlen(msg, msglen); - - while (received < msglen) { - bytes = read(sock, msg->buf + received, msglen - received); - if (bytes < 0) { - if (errno == EAGAIN) - continue; - fatalpe("read"); - } - if (!bytes) - fatal("short message received"); - received += bytes; - } - - return 0; -} diff --git a/src/net.h b/src/net.h deleted file mode 100644 index d4b774eef..000000000 --- a/src/net.h +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include -#include - -typedef struct sockaddr sa; - -extern struct strbuf fqdn; -extern void setup_fqdn(void); -extern void free_fqdn(void); - -enum { - MSG_AUTH = 0x8000000, - MSG_EXPLODE = 0x8000001, -}; - -#define EKERB -2 -#define ELDAP -3 -#define EHOME -4 -#define EQUOTA -5 - -int ceo_receive_message(int sock, struct strbuf *msg, uint32_t *msgtype); -int ceo_send_message(int sock, void *msg, size_t len, uint32_t msgtype); diff --git a/src/op-adduser.c b/src/op-adduser.c deleted file mode 100644 index 87fd3d45d..000000000 --- a/src/op-adduser.c +++ /dev/null @@ -1,335 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "net.h" -#include "ceo.pb-c.h" -#include "config.h" -#include "gss.h" -#include "krb5.h" -#include "ldap.h" -#include "homedir.h" -#include "kadm.h" -#include "daemon.h" -#include "strbuf.h" - -char *prog; - -static const int MAX_MESSAGES = 32; -static const int MAX_MESGSIZE = 512; - -char *user_types[] = { - [CEO__ADD_USER__TYPE__MEMBER] = "member", - [CEO__ADD_USER__TYPE__CLUB] = "club", - [CEO__ADD_USER__TYPE__CLUB_REP] = "clubrep", -}; - -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; -} - -PRINTF_LIKE(2) -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"); - - switch (in->type) { - case CEO__ADD_USER__TYPE__MEMBER: - case CEO__ADD_USER__TYPE__CLUB_REP: - if (!in->password) - return response_message(out, EINVAL, "missing required argument: password"); - break; - - case 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"); - break; - - default: - 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]; - char sasl[1024]; - int user_stat, group_stat, krb_stat, home_stat, quota_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 (snprintf(sasl, sizeof(sasl), "{SASL}%s", - principal) >= sizeof(sasl)) - fatal("sasl overflow"); - - if ((user_stat = ceo_add_user(in->username, ldap_users_base, "member", in->realname, homedir, - member_shell, id, "program", in->program, "userPassword", sasl, 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, NULL, NULL, in->email))) - response_message(out, EHOME, "unable to create home directory for %s", in->username); - else - response_message(out, 0, "successfully created home directory"); - - if ((quota_stat = ceo_set_quota("ctdalek", id))) - response_message(out, EQUOTA, "unable to set quota for %s", in->username); - else - response_message(out, 0, "successfully set quota"); - - return krb_stat || user_stat || group_stat || home_stat || quota_stat; -} - -static int32_t addclub(Ceo__AddUser *in, Ceo__AddUserResponse *out) { - char homedir[1024]; - char acl[64]; - int krb_stat, user_stat, group_stat, sudo_stat, home_stat, quota_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 (snprintf(acl, sizeof(acl), CLUB_ACL, id) >= sizeof(acl)) - fatal("acl overflow"); - - 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, - 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, acl, acl, NULL))) - response_message(out, EHOME, "unable to create home directory for %s", in->username); - else - response_message(out, 0, "successfully created home directory"); - - if ((quota_stat = ceo_set_quota("csc", id))) - response_message(out, EQUOTA, "unable to set quota for %s", in->username); - else - response_message(out, 0, "successfully set quota"); - - return user_stat || group_stat || sudo_stat || home_stat || quota_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_REP) { - status = addmember(in, out); - prog = "addclubrep"; - } 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(NULL, - 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)); - - if (full_write(STDOUT_FILENO, out.buf, out.len)) - fatalpe("write: stdout"); - - ceo__add_user__free_unpacked(in_proto, NULL); - 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, 0); - - configure(); - - if (setenv("KRB5CCNAME", "MEMORY:adduser", 1)) - fatalpe("setenv"); - - ceo_krb5_init(); - ceo_krb5_auth(ldap_admin_principal); - ceo_ldap_init(); - ceo_kadm_init(); - - cmd_adduser(); - - ceo_kadm_cleanup(); - ceo_ldap_cleanup(); - ceo_krb5_deauth(); - ceo_krb5_cleanup(); - - free_config(); - free(prog); - - return 0; -} diff --git a/src/op-mail.c b/src/op-mail.c deleted file mode 100644 index 5e0657e92..000000000 --- a/src/op-mail.c +++ /dev/null @@ -1,217 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "net.h" -#include "ceo.pb-c.h" -#include "config.h" -#include "strbuf.h" - -char *prog; - -static const int MAX_MESSAGES = 32; -static const int MAX_MESGSIZE = 512; - -Ceo__UpdateMailResponse *response_create(void) { - Ceo__UpdateMailResponse *r = xmalloc(sizeof(Ceo__UpdateMailResponse)); - ceo__update_mail_response__init(r); - r->n_messages = 0; - r->messages = xmalloc(MAX_MESSAGES * sizeof(Ceo__StatusMessage *)); - return r; -} - -PRINTF_LIKE(2) -int32_t response_message(Ceo__UpdateMailResponse *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__UpdateMailResponse *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_update_mail(Ceo__UpdateMail *in, Ceo__UpdateMailResponse *out, char *client) { - int client_office = check_group(client, "office"); - int client_syscom = check_group(client, "syscom"); - - notice("update mail uid=%s mail=%s by %s", in->username, in->forward, client); - - if (!in->username) - return response_message(out, EINVAL, "missing required argument: username"); - - int recipient_syscom = check_group(in->username, "syscom"); - - if (!client_syscom && !client_office && strcmp(in->username, client)) - return response_message(out, EPERM, "%s not authorized to update mail", client); - - if (recipient_syscom && !client_syscom) - return response_message(out, EPERM, "denied, recipient is on systems committee"); - - /* don't allow office staff to set complicated forwards; in particular | is a security hole */ - if (in->forward) { - for (char *p = in->forward; *p; p++) { - switch (*p) { - case '"': - case '\'': - case ',': - case '|': - case '$': - case '/': - case '#': - case ':': - return response_message(out, EINVAL, "invalid character in forward: %c", *p); - default: - break; - } - - if (isspace(*p)) - return response_message(out, EINVAL, "invalid character in forward: %c", *p); - } - } - - return 0; -} - -static int32_t update_mail(Ceo__UpdateMail *in, Ceo__UpdateMailResponse *out, char *client) { - int32_t chk_stat; - mode_t mask; - - chk_stat = check_update_mail(in, out, client); - if (chk_stat) - return chk_stat; - - mask = umask(0); - - if (in->forward) { - struct passwd *user = getpwnam(in->username); - - if (!user) - return response_message(out, errno, "getpwnam: %s: %s", in->username, strerror(errno)); - - if (setregid(user->pw_gid, user->pw_gid)) - return response_message(out, errno, "setregid: %s: %s", in->username, strerror(errno)); - if (setreuid(user->pw_uid, user->pw_uid)) - return response_message(out, errno, "setreuid: %s: %s", in->username, strerror(errno)); - - char path[1024]; - - if (snprintf(path, sizeof(path), "%s/.forward", user->pw_dir) >= sizeof(path)) - return response_message(out, ENAMETOOLONG, "homedir is too long"); - - if (unlink(path) && errno != ENOENT) - return response_message(out, errno, "unlink: %s: %s", path, strerror(errno)); - - if (*in->forward) { - int fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0644); - if (fd < 0) - return response_message(out, errno, "open: %s: %s", path, strerror(errno)); - - struct strbuf file_contents = STRBUF_INIT; - strbuf_addf(&file_contents, "%s\n", in->forward); - - if (full_write(fd, file_contents.buf, file_contents.len)) - response_message(out, errno, "write: %s: %s", path, strerror(errno)); - - strbuf_release(&file_contents); - - if (close(fd)) - return response_message(out, errno, "close: %s: %s", path, strerror(errno)); - - response_message(out, 0, "successfully updated forward for %s", in->username); - } else { - response_message(out, 0, "successfully cleared forward for %s", in->username); - } - } - - umask(mask); - - return response_message(out, 0, "finished updating mail for %s", in->username); -} - -void cmd_update_mail(void) { - Ceo__UpdateMail *in_proto; - Ceo__UpdateMailResponse *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__update_mail__unpack(NULL, - in.len, (uint8_t *)in.buf); - if (!in_proto) - fatal("malformed update mail message"); - - char *client = getenv("CEO_USER"); - if (!client) - fatal("environment variable CEO_USER is not set"); - - update_mail(in_proto, out_proto, client); - - strbuf_grow(&out, ceo__update_mail_response__get_packed_size(out_proto)); - strbuf_setlen(&out, ceo__update_mail_response__pack(out_proto, (uint8_t *)out.buf)); - - if (full_write(STDOUT_FILENO, out.buf, out.len)) - fatalpe("write: stdout"); - - ceo__update_mail__free_unpacked(in_proto, NULL); - 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, 0); - - configure(); - - cmd_update_mail(); - - free_config(); - free(prog); - - return 0; -} diff --git a/src/op-mailman b/src/op-mailman deleted file mode 100755 index 312ecd53b..000000000 --- a/src/op-mailman +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/python - -import os, syslog -from subprocess import Popen, PIPE, STDOUT -from ceo import conf -from ceo.ops import get_ceo_user, check_group - -CONFIG_FILE = '/etc/csc/mailman.cf' - -cfg = {} - -def configure(): - string_fields = ['members_list', 'list_domain'] - - # read configuration file - cfg_tmp = conf.read(CONFIG_FILE) - - # verify configuration - conf.check_string_fields(CONFIG_FILE, string_fields, cfg_tmp) - - # update the current configuration with the loaded values - cfg.update(cfg_tmp) - -def main(): - configure() - - remote_user = get_ceo_user() - user_to_add = raw_input() - - if cfg['members_list'] == 'none': - print 'Disabled: %s' % user_to_add - return - - if remote_user == user_to_add or check_group(remote_user, 'office') or check_group(remote_user, 'syscom'): - mailman = Popen(["/opt/mailman3/bin/mailman", "addmembers", "-", "%s@%s" % (cfg['members_list'], cfg['list_domain'])], - stdin=PIPE, stdout=PIPE, stderr=STDOUT) - out, err = mailman.communicate("%s@%s\n" % (user_to_add, cfg['list_domain'])) - syslog.syslog(syslog.LOG_INFO, out) - print out - else: - message = "Access denied: user '%s' cannot subscribe users to %s" % (remote_user, cfg['members_list']) - syslog.syslog(syslog.LOG_NOTICE, message) - print message - -if __name__ == '__main__': - syslog.openlog('op-mailman', syslog.LOG_PID, syslog.LOG_DAEMON) - main() diff --git a/src/op-mysql b/src/op-mysql deleted file mode 100755 index 29a8111df..000000000 --- a/src/op-mysql +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/python - -import os, sys, string, random, syslog, grp, errno, re -from ceo import ceo_pb2, members, conf, ops -from ceo.ops import response_message, get_ceo_user, check_group -import MySQLdb - -CONFIG_FILE = '/etc/csc/mysql.cf' - -cfg = {} - -def configure(): - string_fields = ['mysql_admin_username', 'mysql_admin_password'] - - # read configuration file - cfg_tmp = conf.read(CONFIG_FILE) - - # verify configuration - conf.check_string_fields(CONFIG_FILE, string_fields, cfg_tmp) - - # update the current configuration with the loaded values - cfg.update(cfg_tmp) - -def random_password(): - chars = string.letters + string.digits - return ''.join(random.choice(chars) for i in xrange(20)) - -def check_auth(remote_user, mysql_user, response): - if remote_user == mysql_user: - return response_message(response, 0, 'user %s creating database for self' % remote_user) - club = members.get(mysql_user) - if not club: - return response_message(response, errno.EPERM, 'user %s does not exist' % mysql_user) - if 'club' in club.get('objectClass', []): - if check_group(remote_user, mysql_user): - return response_message(response, 0, 'user %s is in club group %s' % (remote_user, mysql_user)) - elif check_group(remote_user, 'syscom'): - return response_message(response, 0, 'user %s is on systems committee' % remote_user) - else: - return response_message(response, errno.EPERM, 'denied, user %s is not in club group %s' % (remote_user, mysql_user)) - else: - if check_group(remote_user, 'syscom'): - return response_message(response, 0, 'user %s is on systems committee' % remote_user) - else: - return response_message(response, errno.EPERM, 'denied, you may not create databases for other members') - -def mysql_createdb(remote_user, mysql_user, response): - if check_auth(remote_user, mysql_user, response): - return - - response.password = random_password() - - if not re.match('^[a-zA-Z0-9-]+$', mysql_user): - response_message(response, errno.EINVAL, 'invalid characters in username %s' % mysql_user) - return - - if not re.match('^[a-zA-Z0-9-]+$', response.password): - response_message(response, errno.EINVAL, 'invalid characters in password %s' % response.password) - return - - try: - connection = MySQLdb.Connect(user=cfg['mysql_admin_username'], passwd=cfg['mysql_admin_password']) - cursor = connection.cursor() - cursor.execute("GRANT ALL PRIVILEGES ON `%s`.* TO `%s`@`localhost` IDENTIFIED BY '%s'" - % (mysql_user, mysql_user, response.password)) - cursor.execute("CREATE DATABASE IF NOT EXISTS `%s`" % mysql_user) - cursor.close() - connection.close() - - response_message(response, 0, 'successfully created database %s' % mysql_user) - except MySQLdb.MySQLError, e: - response_message(response, 1, 'exception occured creating database: %s' % e) - - -def mysql_op(): - input = sys.stdin.read() - - request = ceo_pb2.AddMySQLUser() - request.ParseFromString(input) - - remote_user = get_ceo_user() - mysql_user = request.username - - response = ceo_pb2.AddMySQLUserResponse() - response_message(response, 0, 'mysql create db=%s by %s' % (mysql_user, remote_user)) - - mysql_createdb(remote_user, mysql_user, response) - - sys.stdout.write(response.SerializeToString()) - -def main(): - configure() - members.configure() - members.connect_anonymous() - syslog.openlog('op-mysql', syslog.LOG_PID, syslog.LOG_DAEMON) - mysql_op() - -if __name__ == '__main__': - main() diff --git a/src/ops.c b/src/ops.c deleted file mode 100644 index 50555807c..000000000 --- a/src/ops.c +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "strbuf.h" -#include "ops.h" -#include "net.h" -#include "util.h" -#include "config.h" - -static struct op *ops; - -static const char *default_op_dir = "/usr/lib/ceod"; -static const char *op_dir; - -static void add_op(char *host, char *name, char *user, 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; - new->user = xstrdup(user); - - 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); - - struct passwd *pw = getpwnam(user); - if (!pw) - fatalpe("cannot add op %s: getpwnam: %s", name, user); - } - - ops = new; - debug("added op %s (%s%s) [%s]", new->name, new->local ? "" : "on ", - new->local ? "local" : host, new->user); -} - -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) != 4) - badconf("%s/%s: expected four words on line %d", op_config_dir, de->d_name, lineno); - - errno = 0; - char *end; - int id = strtol(words[3]->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, words[2]->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->user); - free(ops); - ops = next; - } -} diff --git a/src/ops.h b/src/ops.h deleted file mode 100644 index 69b5b5aa9..000000000 --- a/src/ops.h +++ /dev/null @@ -1,15 +0,0 @@ -struct op { - char *name; - uint32_t id; - int local; - char *hostname; - char *path; - struct in_addr addr; - struct op *next; - char *user; -}; - -void setup_ops(void); -void free_ops(void); -struct op *find_op(const char *name); -struct op *get_local_op(uint32_t id); diff --git a/src/parser.c b/src/parser.c deleted file mode 100644 index 7d462eac1..000000000 --- a/src/parser.c +++ /dev/null @@ -1,215 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "parser.h" -#include "util.h" -#include "config.h" - -#define VAR_MAX 256 - -void config_var(const char *, const char *); - -struct config_file { - FILE *p; - char *name; - int line; - struct config_file *parent; - int comment; -}; - -static void parse_config_file(char *, struct config_file *); -static void parse_error(struct config_file *file, char *msg) { - fatal("parse error on line %d of %s: %s", file->line, file->name, msg); -} - -static int parse_char(struct config_file *file) { - int c = getc(file->p); - if (c == '\n') - (file->line)++; - return c; -} - -static void unparse_char(struct config_file *file, int c) { - if (c == EOF) - return; - ungetc(c, file->p); - if (c == '\n') - (file->line)--; -} - -static void parse_name(struct config_file *file, char *name, size_t maxlen) { - int len = 0; - int c; - - for (;;) { - c = parse_char(file); - - if (c == EOF || c == '\n') { - unparse_char(file, c); - break; - } - - if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') { - unparse_char(file, c); - break; - } - - if (len == maxlen - 1) - parse_error(file, "max name length exceeded"); - - name[len++] = c; - } - - if (len == 0) - parse_error(file, "expected name"); - - name[len++] = '\0'; -} - -static void parse_value(struct config_file *file, char *value, size_t maxlen) { - int len = 0; - int quote = 0; - int comment = 0; - int space = 0; - int c; - - for (;;) { - c = parse_char(file); - - if (c == EOF || c == '\n') - break; - - if (c == '#') - comment = 1; - - if ((isspace(c) && !quote) || comment) { - space = 1; - continue; - } - - if (c == '"') { - quote = ! quote; - continue; - } - - if (len == maxlen - space - 1) - parse_error(file, "max value length exceeded"); - - if (space && len) { - value[len++] = ' '; - } - - space = 0; - value[len++] = c; - } - - if (quote) - parse_error(file, "unbalanced quotes"); - - value[len++] = '\0'; -} - -static void parse_include(struct config_file *file) { - char path[PATH_MAX]; - struct config_file *parent = file->parent; - - parse_value(file, path, sizeof(path)); - - while (parent != NULL) { - if (!strcmp(file->name, parent->name)) - return; - parent = parent->parent; - } - - parse_config_file(path, file); -} - -static void parse_config(struct config_file *file) { - int c; - int comment = 0; - - char var[VAR_MAX]; - char value[VAR_MAX]; - - for (;;) { - c = parse_char(file); - - if (c == '\n') { - comment = 0; - continue; - } - - if (c == EOF) - return; - - if (isspace(c) | comment) - continue; - - if (c == '#') { - comment = 1; - continue; - } - - unparse_char(file, c); - parse_name(file, var, sizeof(var)); - - if (!strcmp(var, "include")) { - parse_include(file); - continue; - } - - for (;;) { - c = parse_char(file); - if (c == EOF || c == '\n') - parse_error(file, "expected '=' before line end"); - if (c == '=') - break; - if (!isspace(c)) - parse_error(file, "expected '='"); - } - - parse_value(file, value, sizeof(value)); - config_var(var, value); - } -} - -static void parse_config_file(char *name, struct config_file *parent) { - struct config_file file; - - file.p = fopen(name, "r"); - file.name = name; - file.line = 1; - file.parent = parent; - - if (!file.p) { - if (parent) - parse_error(parent, strerror(errno)); - else - fatal("failed to open configuration file '%s': %s", name, strerror(errno)); - } - - parse_config(&file); - - fclose(file.p); -} - -long config_long(char *var, char *val) { - char *endptr; - long longval; - - longval = strtol(val, &endptr, 0); - - if (*val == '\0' || *endptr != '\0') - fatal("expected integer value for %s", var); - - return longval; -} - -void config_parse(char *filename) { - debug("loading configuration from %s", filename); - parse_config_file(filename, NULL); -} diff --git a/src/parser.h b/src/parser.h deleted file mode 100644 index 27f8bb92e..000000000 --- a/src/parser.h +++ /dev/null @@ -1,2 +0,0 @@ -void config_parse(char *); -long config_long(char *var, char *val); diff --git a/src/strbuf.c b/src/strbuf.c deleted file mode 100644 index 4d8bc447b..000000000 --- a/src/strbuf.c +++ /dev/null @@ -1,458 +0,0 @@ -/* - Copyright (c) 2005-2008 Junio C. Hamano et al. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License (version 2) - as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include -#include -#include -#include - -#include "strbuf.h" -#include "util.h" - -#define die fatal - -#define alloc_nr(x) (((x)+16)*3/2) - -/* - * * Realloc the buffer pointed at by variable 'x' so that it can hold - * * at least 'nr' entries; the number of entries currently allocated - * * is 'alloc', using the standard growing factor alloc_nr() macro. - * * - * * DO NOT USE any expression with side-effect for 'x' or 'alloc'. - * */ -#define ALLOC_GROW(x, nr, alloc) \ - do { \ - if ((nr) > alloc) { \ - if (alloc_nr(alloc) < (nr)) \ - alloc = (nr); \ - else \ - alloc = alloc_nr(alloc); \ - x = xrealloc((x), alloc * sizeof(*(x))); \ - } \ - } while(0) - - -static inline char *gitstrchrnul(const char *s, int c) -{ - while (*s && *s != c) - s++; - return (char *)s; -} - - -int prefixcmp(const char *str, const char *prefix) -{ - for (; ; str++, prefix++) - if (!*prefix) - return 0; - else if (*str != *prefix) - return (unsigned char)*prefix - (unsigned char)*str; -} - -/* - * Used as the default ->buf value, so that people can always assume - * buf is non NULL and ->buf is NUL terminated even for a freshly - * initialized strbuf. - */ -char strbuf_slopbuf[1]; - -void strbuf_init(struct strbuf *sb, size_t hint) -{ - sb->alloc = sb->len = 0; - sb->buf = strbuf_slopbuf; - if (hint) - strbuf_grow(sb, hint); -} - -void strbuf_release(struct strbuf *sb) -{ - if (sb->alloc) { - free(sb->buf); - strbuf_init(sb, 0); - } -} - -char *strbuf_detach(struct strbuf *sb, size_t *sz) -{ - char *res = sb->alloc ? sb->buf : NULL; - if (sz) - *sz = sb->len; - strbuf_init(sb, 0); - return res; -} - -void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc) -{ - strbuf_release(sb); - sb->buf = buf; - sb->len = len; - sb->alloc = alloc; - strbuf_grow(sb, 0); - sb->buf[sb->len] = '\0'; -} - -void strbuf_grow(struct strbuf *sb, size_t extra) -{ - if (sb->len + extra + 1 <= sb->len) - die("you want to use way too much memory"); - if (!sb->alloc) - sb->buf = NULL; - ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); -} - -void strbuf_trim(struct strbuf *sb) -{ - char *b = sb->buf; - while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1])) - sb->len--; - while (sb->len > 0 && isspace(*b)) { - b++; - sb->len--; - } - memmove(sb->buf, b, sb->len); - sb->buf[sb->len] = '\0'; -} -void strbuf_rtrim(struct strbuf *sb) -{ - while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1])) - sb->len--; - sb->buf[sb->len] = '\0'; -} - -void strbuf_ltrim(struct strbuf *sb) -{ - char *b = sb->buf; - while (sb->len > 0 && isspace(*b)) { - b++; - sb->len--; - } - memmove(sb->buf, b, sb->len); - sb->buf[sb->len] = '\0'; -} - -void strbuf_tolower(struct strbuf *sb) -{ - int i; - for (i = 0; i < sb->len; i++) - sb->buf[i] = tolower(sb->buf[i]); -} - -struct strbuf **strbuf_split(const struct strbuf *sb, int delim) -{ - int alloc = 2, pos = 0; - char *n, *p; - struct strbuf **ret; - struct strbuf *t; - - ret = xcalloc(alloc, sizeof(struct strbuf *)); - p = n = sb->buf; - while (n < sb->buf + sb->len) { - int len; - n = memchr(n, delim, sb->len - (n - sb->buf)); - if (pos + 1 >= alloc) { - alloc = alloc * 2; - ret = xrealloc(ret, sizeof(struct strbuf *) * alloc); - } - if (!n) - n = sb->buf + sb->len - 1; - len = n - p + 1; - t = xmalloc(sizeof(struct strbuf)); - strbuf_init(t, len); - strbuf_add(t, p, len); - ret[pos] = t; - ret[++pos] = NULL; - p = ++n; - } - return ret; -} - -struct strbuf **strbuf_splitws(const struct strbuf *sb) -{ - int alloc = 2, pos = 0; - struct strbuf **ret; - int prev_ws = 1, ws; - int i, start = 0; - - ret = xcalloc(alloc, sizeof(struct strbuf *)); - - for (i = 0; i <= sb->len; i++) { - ws = !sb->buf[i] || isspace(sb->buf[i]); - if (prev_ws == ws) - continue; - if (!ws) { - start = i; - } else { - if (pos + 1 >= alloc) { - alloc = alloc * 2; - ret = xrealloc(ret, sizeof(struct strbuf *) * alloc); - } - ret[pos] = xmalloc(sizeof(struct strbuf)); - strbuf_init(ret[pos], i - start); - strbuf_add(ret[pos], &sb->buf[start], i - start); - pos++; - } - prev_ws = ws; - } - - ret[pos] = NULL; - return ret; -} - -void strbuf_list_free(struct strbuf **sbs) -{ - struct strbuf **s = sbs; - - while (*s) { - strbuf_release(*s); - free(*s++); - } - free(sbs); -} - -size_t strbuf_list_len(struct strbuf **sbs) -{ - size_t n = 0; - while (*sbs++) - n++; - return n; -} - -int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) -{ - int cmp; - if (a->len < b->len) { - cmp = memcmp(a->buf, b->buf, a->len); - return cmp ? cmp : -1; - } else { - cmp = memcmp(a->buf, b->buf, b->len); - return cmp ? cmp : a->len != b->len; - } -} - -void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, - const void *data, size_t dlen) -{ - if (pos + len < pos) - die("you want to use way too much memory"); - if (pos > sb->len) - die("`pos' is too far after the end of the buffer"); - if (pos + len > sb->len) - die("`pos + len' is too far after the end of the buffer"); - - if (dlen >= len) - strbuf_grow(sb, dlen - len); - memmove(sb->buf + pos + dlen, - sb->buf + pos + len, - sb->len - pos - len); - memcpy(sb->buf + pos, data, dlen); - strbuf_setlen(sb, sb->len + dlen - len); -} - -void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) -{ - strbuf_splice(sb, pos, 0, data, len); -} - -void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) -{ - strbuf_splice(sb, pos, len, NULL, 0); -} - -void strbuf_add(struct strbuf *sb, const void *data, size_t len) -{ - strbuf_grow(sb, len); - memcpy(sb->buf + sb->len, data, len); - strbuf_setlen(sb, sb->len + len); -} - -void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) -{ - strbuf_grow(sb, len); - memcpy(sb->buf + sb->len, sb->buf + pos, len); - strbuf_setlen(sb, sb->len + len); -} - -void strbuf_addf(struct strbuf *sb, const char *fmt, ...) -{ - int len; - va_list ap; - - if (!strbuf_avail(sb)) - strbuf_grow(sb, 64); - va_start(ap, fmt); - len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); - va_end(ap); - if (len < 0) - die("your vsnprintf is broken"); - if (len > strbuf_avail(sb)) { - strbuf_grow(sb, len); - va_start(ap, fmt); - len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); - va_end(ap); - if (len > strbuf_avail(sb)) { - die("this should not happen, your snprintf is broken"); - } - } - strbuf_setlen(sb, sb->len + len); -} - -void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list args) -{ - int len; - va_list ap; - - va_copy(ap, args); - if (!strbuf_avail(sb)) - strbuf_grow(sb, 64); - len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); - - va_copy(ap, args); - if (len < 0) - die("your vsnprintf is broken"); - if (len > strbuf_avail(sb)) { - strbuf_grow(sb, len); - len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); - if (len > strbuf_avail(sb)) { - die("this should not happen, your snprintf is broken"); - } - } - strbuf_setlen(sb, sb->len + len); -} - -void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, - void *context) -{ - for (;;) { - const char *percent; - size_t consumed; - - percent = gitstrchrnul(format, '%'); - strbuf_add(sb, format, percent - format); - if (!*percent) - break; - format = percent + 1; - - consumed = fn(sb, format, context); - if (consumed) - format += consumed; - else - strbuf_addch(sb, '%'); - } -} - -size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, - void *context) -{ - struct strbuf_expand_dict_entry *e = context; - size_t len; - - for (; e->placeholder && (len = strlen(e->placeholder)); e++) { - if (!strncmp(placeholder, e->placeholder, len)) { - if (e->value) - strbuf_addstr(sb, e->value); - return len; - } - } - return 0; -} - -size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) -{ - size_t res; - - strbuf_grow(sb, size); - res = fread(sb->buf + sb->len, 1, size, f); - if (res > 0) { - strbuf_setlen(sb, sb->len + res); - } - return res; -} - -ssize_t strbuf_write(struct strbuf *sb, int fd) { - ssize_t total = 0; - - while (total < sb->len) { - ssize_t cnt = write(fd, sb->buf + total, sb->len - total); - if (cnt <= 0) - return -1; - total += cnt; - } - - return total; -} - -ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) -{ - size_t oldlen = sb->len; - - strbuf_grow(sb, hint ? hint : 8192); - for (;;) { - ssize_t cnt; - - cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); - if (cnt < 0) { - strbuf_setlen(sb, oldlen); - return -1; - } - if (!cnt) - break; - sb->len += cnt; - strbuf_grow(sb, 8192); - } - - sb->buf[sb->len] = '\0'; - return sb->len - oldlen; -} - -int strbuf_getline(struct strbuf *sb, FILE *fp, int term) -{ - int ch; - - strbuf_grow(sb, 0); - if (feof(fp)) - return EOF; - - strbuf_reset(sb); - while ((ch = fgetc(fp)) != EOF) { - if (ch == term) - break; - strbuf_grow(sb, 1); - sb->buf[sb->len++] = ch; - } - if (ch == EOF && sb->len == 0) - return EOF; - - sb->buf[sb->len] = '\0'; - return 0; -} - -int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) -{ - int fd, len; - - fd = open(path, O_RDONLY); - if (fd < 0) - return -1; - len = strbuf_read(sb, fd, hint); - close(fd); - if (len < 0) - return -1; - - return len; -} diff --git a/src/strbuf.h b/src/strbuf.h deleted file mode 100644 index fe9f52896..000000000 --- a/src/strbuf.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - Copyright (c) 2005-2008 Junio C. Hamano et al. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License (version 2) - as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef STRBUF_H -#define STRBUF_H - -/* - * Strbuf's can be use in many ways: as a byte array, or to store arbitrary - * long, overflow safe strings. - * - * Strbufs has some invariants that are very important to keep in mind: - * - * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to - * build complex strings/buffers whose final size isn't easily known. - * - * It is NOT legal to copy the ->buf pointer away. - * `strbuf_detach' is the operation that detachs a buffer from its shell - * while keeping the shell valid wrt its invariants. - * - * 2. the ->buf member is a byte array that has at least ->len + 1 bytes - * allocated. The extra byte is used to store a '\0', allowing the ->buf - * member to be a valid C-string. Every strbuf function ensure this - * invariant is preserved. - * - * Note that it is OK to "play" with the buffer directly if you work it - * that way: - * - * strbuf_grow(sb, SOME_SIZE); - * ... Here, the memory array starting at sb->buf, and of length - * ... strbuf_avail(sb) is all yours, and you are sure that - * ... strbuf_avail(sb) is at least SOME_SIZE. - * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE); - * - * Of course, SOME_OTHER_SIZE must be smaller or equal to strbuf_avail(sb). - * - * Doing so is safe, though if it has to be done in many places, adding the - * missing API to the strbuf module is the way to go. - * - * XXX: do _not_ assume that the area that is yours is of size ->alloc - 1 - * even if it's true in the current implementation. Alloc is somehow a - * "private" member that should not be messed with. - */ - -#include -#include -#include -#include -#include - -extern char strbuf_slopbuf[]; -struct strbuf { - size_t alloc; - size_t len; - char *buf; -}; - -#define STRBUF_INIT { 0, 0, strbuf_slopbuf } - -/*----- strbuf life cycle -----*/ -extern void strbuf_init(struct strbuf *, size_t); -extern void strbuf_release(struct strbuf *); -extern char *strbuf_detach(struct strbuf *, size_t *); -extern void strbuf_attach(struct strbuf *, void *, size_t, size_t); -static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) { - struct strbuf tmp = *a; - *a = *b; - *b = tmp; -} - -/*----- strbuf size related -----*/ -static inline size_t strbuf_avail(const struct strbuf *sb) { - return sb->alloc ? sb->alloc - sb->len - 1 : 0; -} - -extern void strbuf_grow(struct strbuf *, size_t); - -static inline void strbuf_setlen(struct strbuf *sb, size_t len) { - if (!sb->alloc) - strbuf_grow(sb, 0); - assert(len < sb->alloc); - sb->len = len; - sb->buf[len] = '\0'; -} -#define strbuf_reset(sb) strbuf_setlen(sb, 0) - -/*----- content related -----*/ -extern void strbuf_trim(struct strbuf *); -extern void strbuf_rtrim(struct strbuf *); -extern void strbuf_ltrim(struct strbuf *); -extern int strbuf_cmp(const struct strbuf *, const struct strbuf *); -extern void strbuf_tolower(struct strbuf *); - -extern struct strbuf **strbuf_split(const struct strbuf *, int delim); -extern struct strbuf **strbuf_splitws(const struct strbuf *); -extern void strbuf_list_free(struct strbuf **); -extern size_t strbuf_list_len(struct strbuf **); - -/*----- add data in your buffer -----*/ -static inline void strbuf_addch(struct strbuf *sb, int c) { - strbuf_grow(sb, 1); - sb->buf[sb->len++] = c; - sb->buf[sb->len] = '\0'; -} - -extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t); -extern void strbuf_remove(struct strbuf *, size_t pos, size_t len); - -/* splice pos..pos+len with given data */ -extern void strbuf_splice(struct strbuf *, size_t pos, size_t len, - const void *, size_t); - -extern void strbuf_add(struct strbuf *, const void *, size_t); -static inline void strbuf_addstr(struct strbuf *sb, const char *s) { - strbuf_add(sb, s, strlen(s)); -} -static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) { - strbuf_add(sb, sb2->buf, sb2->len); -} -extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len); - -typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context); -extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context); -struct strbuf_expand_dict_entry { - const char *placeholder; - const char *value; -}; -extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context); - -__attribute__((format(printf,2,3))) -extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); -extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap); - -extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); -/* XXX: if read fails, any partial read is undone */ -extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); -extern ssize_t strbuf_write(struct strbuf *sb, int fd); -extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint); - -extern int strbuf_getline(struct strbuf *, FILE *, int); - -extern void stripspace(struct strbuf *buf, int skip_comments); -extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env); - -#endif /* STRBUF_H */ diff --git a/src/util.c b/src/util.c deleted file mode 100644 index be862d989..000000000 --- a/src/util.c +++ /dev/null @@ -1,305 +0,0 @@ -#define _ATFILE_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "strbuf.h" - -static int log_stderr = 1; -static int log_maxprio = LOG_DEBUG; - -void init_log(const char *ident, int option, int facility, int lstderr) { - openlog(ident, option, facility); - log_stderr = lstderr || isatty(STDERR_FILENO); -} - -void log_set_maxprio(int prio) { - log_maxprio = prio; -} - -static void errmsg(int prio, const char *prefix, const char *fmt, va_list args) { - struct strbuf msg = STRBUF_INIT; - - strbuf_addf(&msg, "%s: ", prefix); - strbuf_vaddf(&msg, fmt, args); - strbuf_addch(&msg, '\n'); - - syslog(prio, "%s", msg.buf); - if (log_stderr && prio <= log_maxprio) - fputs(msg.buf, stderr); - - strbuf_release(&msg); -} - -static void errmsgpe(int prio, const char *prefix, const char *fmt, va_list args) { - struct strbuf msg = STRBUF_INIT; - - strbuf_addf(&msg, "%s: ", prefix); - strbuf_vaddf(&msg, fmt, args); - strbuf_addf(&msg, ": %s\n", strerror(errno)); - - syslog(prio, "%s", msg.buf); - if (log_stderr && prio <= log_maxprio) - fputs(msg.buf, stderr); - - strbuf_release(&msg); -} - -NORETURN static void die(int prio, const char *prefix, const char *msg, va_list args) { - errmsg(prio, prefix, msg, args); - exit(1); -} - -NORETURN static void diepe(int prio, const char *prefix, const char *msg, va_list args) { - errmsgpe(prio, prefix, msg, args); - exit(1); -} - -NORETURN void fatal(const char *msg, ...) { - va_list args; - va_start(args, msg); - die(LOG_CRIT, "fatal", msg, args); - va_end(args); -} - -void error(const char *msg, ...) { - va_list args; - va_start(args, msg); - errmsg(LOG_ERR, "error", msg, args); - va_end(args); -} - -void warn(const char *msg, ...) { - va_list args; - va_start(args, msg); - errmsg(LOG_WARNING, "warning", msg, args); - va_end(args); -} - -void notice(const char *msg, ...) { - va_list args; - va_start(args, msg); - errmsg(LOG_NOTICE, "notice", msg, args); - va_end(args); -} - -void debug(const char *msg, ...) { - va_list args; - va_start(args, msg); - errmsg(LOG_DEBUG, "debug", msg, args); - va_end(args); -} - -void logmsg(int priority, const char *msg, ...) { - va_list args; - va_start(args, msg); - vsyslog(priority, msg, args); - va_end(args); - va_start(args, msg); - if (log_stderr && priority <= log_maxprio) { - vfprintf(stderr, msg, args); - fputc('\n', stderr); - } - va_end(args); -} - -NORETURN void deny(const char *msg, ...) { - va_list args; - va_start(args, msg); - die(LOG_ERR, "denied", msg, args); - va_end(args); -} - -NORETURN void badconf(const char *msg, ...) { - va_list args; - va_start(args, msg); - die(LOG_CRIT, "configuration error", msg, args); - va_end(args); -} - -NORETURN void fatalpe(const char *msg, ...) { - va_list args; - va_start(args, msg); - diepe(LOG_CRIT, "fatal", msg, args); - va_end(args); -} - -void errorpe(const char *msg, ...) { - va_list args; - va_start(args, msg); - errmsgpe(LOG_ERR, "error", msg, args); - va_end(args); -} - -void warnpe(const char *msg, ...) { - va_list args; - va_start(args, msg); - errmsgpe(LOG_WARNING, "warning", msg, args); - va_end(args); -} - -int spawnv(const char *path, char *const argv[]) { - int pid, status; - - fflush(stdout); - fflush(stderr); - - pid = fork(); - if (pid < 0) - fatalpe("fork"); - else if (pid) - waitpid(pid, &status, 0); - else - exit(execv(path, argv)); - return status; -} - -int 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) - return wcount; - total += wcount; - } - - return 0; -} - -int spawnvem(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr) { - return spawnvemu(path, argv, envp, output, input, cap_stderr, NULL); -} - -int spawnvemu(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr, char *user) { - 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]); - - if (user) { - struct passwd *pw = getpwnam(user); - if (!pw) - fatalpe("getpwnam: %s", user); - if (initgroups(user, pw->pw_gid)) - fatalpe("initgroups: %s", user); - if (setregid(pw->pw_gid, pw->pw_gid)) - fatalpe("setregid: %s", user); - if (setreuid(pw->pw_uid, pw->pw_uid)) - fatalpe("setreuid"); - } - execve(path, argv, envp); - fatalpe("execve"); - } else { - close(tochild[0]); - close(fmchild[1]); - full_write(tochild[1], output->buf, output->len); - close(tochild[1]); - - if (input) - strbuf_read(input, fmchild[0], 0); - close(fmchild[0]); - } - - wpid = waitpid(pid, &status, 0); - if (wpid < 0) - fatalpe("waitpid"); - else if (wpid != pid) - fatal("waitpid is broken"); - - if (WIFEXITED(status) && WEXITSTATUS(status)) - notice("child %s exited with status %d", path, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - notice("child %s killed by signal %d", path, WTERMSIG(status)); - - return status; -} - -int spawnv_msg(const char *path, char *const *argv, const struct strbuf *output) { - return spawnvem(path, argv, environ, output, NULL, 0); -} - -int check_group(char *username, char *group) { - struct group *grp = getgrnam(group); - char **members; - - if (grp) - for (members = grp->gr_mem; *members; members++) - if (!strcmp(username, *members)) - return 1; - - return 0; -} - -FILE *fopenat(DIR *d, const char *path, int flags) { - int dfd = dirfd(d); - if (dfd < 0) - return NULL; - int fd = openat(dfd, path, flags); - if (fd < 0) - return NULL; - return fdopen(fd, flags & O_RDWR ? "r+" : - flags & O_WRONLY ? "w" : - "r"); -} - -void make_env(char **envp, ...) { - const size_t len = 4096; - size_t used = 0; - int args = 0; - char *buf = xmalloc(len); - va_list ap; - va_start(ap, envp); - char *name, *val; - - while ((name = va_arg(ap, char *))) { - val = va_arg(ap, char *); - if (!val) - continue; - int n = snprintf(buf + used, len - used, "%s=%s", name, val); - if (n < 0) - fatalpe("snprintf"); - if (n >= len - used) - fatal("environment too big"); - - envp[args++] = buf + used; - used += n + 1; - } - - if (!args) - free(buf); - - envp[args] = NULL; -} - -void free_env(char **envp) { - free(*envp); -} diff --git a/src/util.h b/src/util.h deleted file mode 100644 index 489dff151..000000000 --- a/src/util.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef CEO_UTIL_H -#define CEO_UTIL_H - -#include -#include -#include -#include -#include -#include -#include - -#include "strbuf.h" - -#ifdef __GNUC__ -#define NORETURN __attribute__((__noreturn__)) -#define PRINTF_LIKE(extra) __attribute__((format(printf, extra+1, extra+2))) -#else -#define NORETURN -#define PRINTF_LIKE(extra) -#endif - -#ifndef LOG_AUTHPRIV -#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); -int spawnvemu(const char *path, char *const *argv, char *const *envp, const struct strbuf *output, struct strbuf *input, int cap_stderr, char *user); -int 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 lstderr); -int check_group(char *username, char *group); -void log_set_maxprio(int prio); - -PRINTF_LIKE(0) NORETURN void fatal(const char *, ...); -PRINTF_LIKE(0) NORETURN void fatalpe(const char *, ...); -PRINTF_LIKE(0) NORETURN void badconf(const char *, ...); -PRINTF_LIKE(0) NORETURN void deny(const char *, ...); -PRINTF_LIKE(0) void error(const char *, ...); -PRINTF_LIKE(0) void warn(const char *, ...); -PRINTF_LIKE(0) void notice(const char *, ...); -PRINTF_LIKE(0) void debug(const char *, ...); -PRINTF_LIKE(0) void errorpe(const char *, ...); -PRINTF_LIKE(0) void warnpe(const char *, ...); -PRINTF_LIKE(1) void logmsg(int priority, const char *, ...); - -static inline void *xmalloc(size_t size) { - void *alloc = malloc(size); - - if (alloc == NULL) - fatal("out of memory"); - - return alloc; -} - -static inline void *xrealloc(void *ptr, size_t size) { - void *alloc = realloc(ptr, size); - - if (alloc == NULL) - fatal("out of memory"); - - return alloc; -} - -static inline void *xcalloc(size_t nmemb, size_t size) { - void *alloc = calloc(nmemb, size); - - if (alloc == NULL) - fatal("out of memory"); - - return alloc; -} - -static inline char *xstrdup(const char *s) { - char *dup = strdup(s); - - if (dup == NULL) - fatal("out of memory"); - - return dup; -} - -#endif