diff options
author | wiktor w brodlo <wiktor@brodlo.net> | 2011-06-15 16:59:54 +0000 |
---|---|---|
committer | wiktor w brodlo <wiktor@brodlo.net> | 2011-06-15 16:59:54 +0000 |
commit | 2590d96369d0217e31dc2812690dde61dac417b5 (patch) | |
tree | 82276f787b08a28548e342c7921486f1acefab9f /users.py | |
parent | first commit (diff) | |
download | anaconda-2590d96369d0217e31dc2812690dde61dac417b5.tar.gz anaconda-2590d96369d0217e31dc2812690dde61dac417b5.tar.bz2 anaconda-2590d96369d0217e31dc2812690dde61dac417b5.zip |
Initial import from Sabayon (ver 0.9.9.56)
Diffstat (limited to 'users.py')
-rw-r--r-- | users.py | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/users.py b/users.py new file mode 100644 index 0000000..a940a9a --- /dev/null +++ b/users.py @@ -0,0 +1,322 @@ +# +# users.py: Code for creating user accounts and setting the root password +# +# Copyright (C) 2006, 2007, 2008 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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, see <http://www.gnu.org/licenses/>. +# +# Author(s): Chris Lumens <clumens@redhat.com> +# + +import libuser +import string +import crypt +import random +import tempfile +import os +import os.path + +import logging +log = logging.getLogger("anaconda") + +def createLuserConf(instPath, algoname='sha512'): + """Writes a libuser.conf for instPath.""" + createTmp = False + try: + fn = os.environ["LIBUSER_CONF"] + if os.access(fn, os.F_OK): + log.info("removing libuser.conf at %s" % (os.getenv("LIBUSER_CONF"))) + os.unlink(fn) + log.info("created new libuser.conf at %s with instPath=\"%s\"" % \ + (fn,instPath)) + fd = open(fn, 'w') + except: + createTmp = True + + if createTmp: + (fp, fn) = tempfile.mkstemp(prefix="libuser.") + log.info("created new libuser.conf at %s with instPath=\"%s\"" % \ + (fn,instPath)) + fd = os.fdopen(fp, 'w') + + buf = """ +[defaults] +skeleton = %(instPath)s/etc/skel +mailspooldir = %(instPath)s/var/mail +crypt_style = %(algo)s +modules = files shadow +create_modules = files shadow +[files] +directory = %(instPath)s/etc +[shadow] +directory = %(instPath)s/etc +""" % {"instPath": instPath, "algo": algoname} + + fd.write(buf) + fd.close() + os.environ["LIBUSER_CONF"] = fn + + return fn + +# These are explained in crypt/crypt-entry.c in glibc's code. The prefixes +# we use for the different crypt salts: +# $1$ MD5 +# $5$ SHA256 +# $6$ SHA512 +def cryptPassword(password, algo=None): + salts = {'md5': '$1$', 'sha256': '$5$', 'sha512': '$6$'} + saltlen = 2 + + if algo is None: + algo = 'sha512' + + if algo == 'md5' or algo == 'sha256' or algo == 'sha512': + saltlen = 16 + + saltstr = salts[algo] + + for i in range(saltlen): + saltstr = saltstr + random.choice (string.letters + + string.digits + './') + + return crypt.crypt (password, saltstr) + +class Users: + def __init__ (self, anaconda): + self.anaconda = anaconda + self.admin = libuser.admin() + self.rootPassword = { "isCrypted": False, "password": "", "lock": False } + # dict composed by username as key + # 'fullname' => full name + # 'password' => password + # 'groups' => list of groups + # 'group' => main group + self.otherUsers = {} + + def createGroup (self, name=None, gid=None, root="/mnt/sysimage"): + childpid = os.fork() + + if not childpid: + if not root in ["","/"]: + os.chroot(root) + del(os.environ["LIBUSER_CONF"]) + + self.admin = libuser.admin() + + try: + if self.admin.lookupGroupByName(name): + os._exit(1) + + groupEnt = self.admin.initGroup(name) + + if gid >= 0: + groupEnt.set(libuser.GIDNUMBER, gid) + + self.admin.addGroup(groupEnt) + os._exit(0) + except Exception, e: + log.critical("Error when creating new group: %s" % str(e)) + os._exit(1) + + try: + (pid, status) = os.waitpid(childpid, 0) + except OSError as e: + log.critical("exception from waitpid while creating a group: %s %s" % (e.errno, e.strerror)) + return False + + if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0): + return True + else: + return False + + def createUser (self, name=None, password=None, isCrypted=False, groups=[], + homedir=None, shell=None, uid=None, algo=None, lock=False, + root="/mnt/sysimage", gecos=None): + childpid = os.fork() + + if not childpid: + if not root in ["","/"]: + os.chroot(root) + del(os.environ["LIBUSER_CONF"]) + + self.admin = libuser.admin() + + try: + if self.admin.lookupUserByName(name): + os._exit(1) + + userEnt = self.admin.initUser(name) + groupEnt = self.admin.initGroup(name) + + grpLst = filter(lambda grp: grp, + map(lambda name: self.admin.lookupGroupByName(name), groups)) + userEnt.set(libuser.GIDNUMBER, [groupEnt.get(libuser.GIDNUMBER)[0]] + + map(lambda grp: grp.get(libuser.GIDNUMBER)[0], grpLst)) + + if not homedir: + homedir = "/home/" + name + + userEnt.set(libuser.HOMEDIRECTORY, homedir) + + if shell: + userEnt.set(libuser.LOGINSHELL, shell) + + if uid >= 0: + userEnt.set(libuser.UIDNUMBER, uid) + + if gecos: + userEnt.set(libuser.GECOS, gecos) + + self.admin.addUser(userEnt) + self.admin.addGroup(groupEnt) + + if password: + if isCrypted: + self.admin.setpassUser(userEnt, password, True) + else: + self.admin.setpassUser(userEnt, + cryptPassword(password, algo=algo), + True) + + if lock: + self.admin.lockUser(userEnt) + + # Add the user to all the groups they should be part of. + grpLst.append(self.admin.lookupGroupByName(name)) + for grp in grpLst: + grp.add(libuser.MEMBERNAME, name) + self.admin.modifyGroup(grp) + + os._exit(0) + except Exception, e: + log.critical("Error when creating new user: %s" % str(e)) + os._exit(1) + + try: + (pid, status) = os.waitpid(childpid, 0) + except OSError as e: + log.critical("exception from waitpid while creating a user: %s %s" % (e.errno, e.strerror)) + return False + + if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0): + return True + else: + return False + + def checkUserExists(self, username, root="/mnt/sysimage"): + childpid = os.fork() + + if not childpid: + if not root in ["","/"]: + os.chroot(root) + del(os.environ["LIBUSER_CONF"]) + + self.admin = libuser.admin() + + try: + if self.admin.lookupUserByName(username): + os._exit(0) + except Exception, e: + log.critical("Error when searching for user: %s" % str(e)) + os._exit(1) + + try: + (pid, status) = os.waitpid(childpid, 0) + except OSError as e: + log.critical("exception from waitpid while creating a user: %s %s" % (e.errno, e.strerror)) + return False + + if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0): + return True + else: + return False + + # Reads the auth string and returns a string indicating our desired + # password encoding algorithm. + def getPassAlgo(self): + if self.anaconda.security.auth.find("--enablemd5") != -1 or \ + self.anaconda.security.auth.find("--passalgo=md5") != -1: + return 'md5' + elif self.anaconda.security.auth.find("--passalgo=sha256") != -1: + return 'sha256' + elif self.anaconda.security.auth.find("--passalgo=sha512") != -1: + return 'sha512' + else: + return None + + def setUserPassword(self, username, password, isCrypted, lock, algo=None): + user = self.admin.lookupUserByName(username) + + if isCrypted: + self.admin.setpassUser(user, password, True) + else: + self.admin.setpassUser(user, cryptPassword(password, algo=algo), True) + + if lock: + self.admin.lockUser(user) + + self.admin.modifyUser(user) + + def setRootPassword(self, algo=None): + return self.setUserPassword("root", self.rootPassword["password"], + self.rootPassword["isCrypted"], + self.rootPassword["lock"], algo) + + def write(self, instPath): + # make sure crypt_style in libuser.conf matches the salt we're using + createLuserConf(instPath, algoname=self.getPassAlgo()) + + # User should already exist, just without a password. + self.setRootPassword(algo=self.getPassAlgo()) + + for user, udata in self.otherUsers.items(): + self.createUser(name=udata['username'], + password=udata['password'], + groups=udata['groups'], + algo=self.getPassAlgo(), + gecos=udata.get('fullname', None), + root=instPath + ) + + if self.anaconda.ksdata: + for gd in self.anaconda.ksdata.group.groupList: + if not self.createGroup(name=gd.name, + gid=gd.gid, + root=instPath): + log.error("Group %s already exists, not creating." % gd.name) + + for ud in self.anaconda.ksdata.user.userList: + if not self.createUser(name=ud.name, + password=ud.password, + isCrypted=ud.isCrypted, + groups=ud.groups, + homedir=ud.homedir, + shell=ud.shell, + uid=ud.uid, + algo=self.getPassAlgo(), + lock=ud.lock, + root=instPath, + gecos=ud.gecos): + log.error("User %s already exists, not creating." % ud.name) + + def writeKS(self, f): + if self.rootPassword["isCrypted"]: + args = " --iscrypted %s" % self.rootPassword["password"] + else: + args = " --iscrypted %s" % cryptPassword(self.rootPassword["password"], algo=self.getPassAlgo()) + + if self.rootPassword["lock"]: + args += " --lock" + + f.write("rootpw %s\n" % args) |