123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- # -*- coding: utf-8 -*-
- #
- # Copyright (C) 2006-2010 TUBITAK/UEKAE
- #
- # 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.
- #
- import os
- import glob
- import fcntl
- import random
- import shutil
- import hashlib
- from string import ascii_letters, digits
- from pardus.fileutils import FileLock
- try:
- import polkit
- except ImportError:
- pass
- # faces
- FACES = [
- "/usr/share/kde4/apps/kdm/pics/users",
- ]
- # messages
- invalid_username_msg = {
- "en": "User name is invalid.",
- "tr": "Kullanıcı adı geçersiz.",
- "fr": "Nom d'utilisateur invalide.",
- "es": "Nombre de usuario no es válido.",
- "de": "Benutzername nicht erlaubt.",
- "nl": "Gebruikernaam is ongeldig.",
- }
- invalid_realname_msg = {
- "en": "Real name is invalid.",
- "tr": "Gerçek isim geçersiz.",
- "fr": "Nom réel invalide.",
- "es": "Nombre real no es válido.",
- "de": "Dieser vollständige Name ist nicht erlaubt.",
- "nl": "Echte naam is ongeldig.",
- }
- short_password_msg = {
- "en": "Password is too short.",
- "tr": "Parola çok kısa.",
- "fr": "Mot de passe trop court.",
- "es": "Contraseña es demadiado corta.",
- "de": "Passwort ist zu kurz.",
- "nl": "Wachtwoord is te kort.",
- }
- name_password_msg = {
- "en": "Dont use your name as a password.",
- "tr": "Adınızı parola olarak kullanmayın.",
- "fr": "N'utilisez pas votre nom comme mot de passe.",
- "es": "No use su nombre como contraseña.",
- "de": "Benutzen Sie nicht Ihren Namen als Passwort.",
- "nl": "Uw naam niet als wachtwoord gebruiken.",
- }
- invalid_group_msg = {
- "en": "Invalid group name:",
- "tr": "Geçersiz grup adı:",
- "fr": "Nom de groupe invalide:",
- "es": "Nombre de grupo inválido:",
- "de": "Gruppenname nicht erlaubt:",
- "nl": "Ongeldige groepnaam:",
- }
- invalid_userid_msg = {
- "en": "Invalid user ID.",
- "tr": "Geçersiz kullanıcı numarası.",
- "fr": "Identifiant utilisateur invalide.",
- "es": "ID de usuario no válido.",
- "de": "User-ID nicht erlaubt.",
- "nl": "Gebruiker-id is ongeldig.",
- }
- used_userid_msg = {
- "en": "This user ID is already used.",
- "tr": "Bu kullanıcı numarası zaten kullanılmakta.",
- "fr": "Cet identifiant utilisateur est déjà utilisé.",
- "es": "Este ID de usuario ya está en uso.",
- "de": "Dieser user-ID is schon vergeben.",
- "nl": "Deze gebruiker-id is reeds in gebruik.",
- }
- used_username_msg = {
- "en": "This user name is already used.",
- "tr": "Bu kullanıcı adı zaten kullanılmakta.",
- "fr": "Ce nom d'utilisateur est déjà utilisé.",
- "es": "Este nombre de usuario ya está en uso.",
- "de": "Dieser Benutzername is schon vergeben.",
- "nl": "Deze gebruikernaam is reeds in gebruik.",
- }
- no_group_msg = {
- "en": "No such group exists.",
- "tr": "Böyle bir grup yok.",
- "fr": "Il n'existe aucun groupe de ce nom-là.",
- "es": "No existe el grupo.",
- "de": "Diese Gruppe gibt es nicht.",
- "nl": "Deze groep bestaat niet.",
- }
- no_user_msg = {
- "en": "No user with given ID.",
- "tr": "Verilen numaralı bir kullanıcı yok.",
- "fr": "Il n'existe aucun utilisateur avec et identifiant.",
- "es": "No existe usuario con éste ID.",
- "de": "Es gibt keien Benutzer mit dem angegebenen ID.",
- "nl": "Gebruiker met opgegeven ID bestaat niet.",
- }
- delete_root_msg = {
- "en": "You cant delete root user.",
- "tr": "Kök kullanıcıyı silemezsiniz.",
- "fr": "Vous ne pouvez pas supprimer l'administrateur.",
- "es": "No se puede eliminar al usuario root.",
- "de": "Benutzer ROOT darf nicht gelöscht werden.",
- "nl": "Systeembeheerder (root) kan niet verwijderd worden.",
- }
- invalid_groupid_msg = {
- "en": "Invalid group ID.",
- "tr": "Geçersiz grup numarası.",
- "fr": "Identifiant de groupe invalide.",
- "es": "ID del grupo inválido.",
- "de": "Gruppen-ID nicht zulässig.",
- "nl": "Groep-ID is ongeldig.",
- }
- used_groupid_msg = {
- "en": "This group ID is already used.",
- "tr": "Bu grup numarası zaten kullanılmakta.",
- "fr": "Cet identifiant de groupe est déjà utilisé.",
- "es": "Este ID de grupo ya está en uso.",
- "de": "Dieser Gruppen-ID is schon vergeben.",
- "nl": "Deze groep-ID is reeds in gebruik.",
- }
- used_groupname_msg = {
- "en": "This group name is already used.",
- "tr": "Bu grup adı zaten kullanılmakta.",
- "fr": "Ce nom de groupe est déjà utilisé.",
- "es": "Este nombre de grupo ya está en uso.",
- "de": "Dieser Gruppennam is schon vergeben.",
- "nl": "Deze groepnaam is reeds in gebruik.",
- }
- # parameters
- uid_minimum = 1000
- uid_maximum = 65000
- #
- def setFace(uid, homedir):
- files = []
- for directory in FACES:
- if os.path.exists(directory):
- for filename in os.listdir(directory):
- if filename.endswith(".png"):
- files.append(os.path.join(directory, filename))
- if len(files):
- icon = os.path.join(homedir, ".face.icon")
- shutil.copy(random.choice(files), icon)
- os.chmod(icon, 0644)
- os.chown(icon, uid, 100)
- def checkName(name):
- first_valid = ascii_letters
- valid = ascii_letters + "_-" + digits
- if len(name) == 0 or len(filter(lambda x: not x in valid, name)) != 0 or not name[0] in first_valid:
- fail(_(invalid_username_msg))
- def checkRealName(realname):
- if len(filter(lambda x: x == "\n" or x == ":", realname)) != 0:
- fail(_(invalid_realname_msg))
- def checkPassword(password, badlist):
- if len(password) < 1:
- fail(_(short_password_msg))
- if password in badlist:
- fail(_(name_password_msg))
- def checkGroupName(name):
- valid = ascii_letters + "_-"
- if name == "" or len(filter(lambda x: not x in valid, name)) != 0:
- fail(_(invalid_group_msg) + " " + name)
- #
- class User:
- def __init__(self):
- self.password = None
- def __str__(self):
- return "%s (%d, %d)\n %s\n %s\n %s\n %s" % (
- self.name, self.uid, self.gid,
- self.realname, self.homedir, self.shell,
- self.password
- )
- class Group:
- def __str__(self):
- s = "%s (%d)" % (self.name, self.gid)
- for name in self.members:
- s += "\n %s" % name
- return s
- class Database:
- passwd_path = "/etc/passwd"
- shadow_path = "/etc/shadow"
- group_path = "/etc/group"
- lock_path = "/etc/.pwd.lock"
- def __init__(self, for_read=False):
- self.lock = FileLock(self.lock_path)
- self.lock.lock(shared=for_read)
- self.users = {}
- self.users_by_name = {}
- self.groups = {}
- self.groups_by_name = {}
- for line in file(self.passwd_path):
- if line != "" and line != "\n":
- parts = line.rstrip("\n").split(":")
- user = User()
- user.name = parts[0]
- user.uid = int(parts[2])
- user.gid = int(parts[3])
- user.realname = parts[4]
- user.homedir = parts[5]
- user.shell = parts[6]
- self.users[user.uid] = user
- self.users_by_name[user.name] = user
- for line in file(self.shadow_path):
- if line != "" and line != "\n":
- parts = line.rstrip("\n").split(":")
- if self.users_by_name.has_key(parts[0]):
- user = self.users_by_name[parts[0]]
- user.password = parts[1]
- user.pwrest = parts[2:]
- for line in file(self.group_path):
- if line != "" and line != "\n":
- parts = line.rstrip("\n").split(":")
- group = Group()
- group.name = parts[0]
- group.gid = int(parts[2])
- group.members = parts[3].split(",")
- if "" in group.members:
- group.members.remove("")
- self.groups[group.gid] = group
- self.groups_by_name[group.name] = group
- def sync(self):
- lines = []
- keys = self.users.keys()
- keys.sort()
- for uid in keys:
- user = self.users[uid]
- lines.append("%s:x:%d:%d:%s:%s:%s\n" % (
- user.name, uid, user.gid,
- user.realname, user.homedir, user.shell
- ))
- f = file(self.passwd_path, "w")
- f.writelines(lines)
- f.close()
- lines = []
- keys = self.users.keys()
- keys.sort()
- for uid in keys:
- user = self.users[uid]
- if user.password:
- lines.append("%s:%s:%s\n" % (
- user.name,
- user.password,
- ":".join(user.pwrest)
- ))
- else:
- lines.append("%s::13094:0:99999:7:::\n" % user.name)
- f = file(self.shadow_path, "w")
- f.writelines(lines)
- f.close()
- lines = []
- keys = self.groups.keys()
- keys.sort()
- for gid in keys:
- group = self.groups[gid]
- lines.append("%s:x:%s:%s\n" % (group.name, gid, ",".join(group.members)))
- f = file(self.group_path, "w")
- f.writelines(lines)
- f.close()
- def set_groups(self, name, grouplist):
- for gid in self.groups.keys():
- g = self.groups[gid]
- if name in g.members:
- if not g.name in grouplist:
- g.members.remove(name)
- else:
- if g.name in grouplist:
- g.members.append(name)
- def next_uid(self):
- for i in range(uid_minimum, uid_maximum):
- if not self.users.has_key(i):
- return i
- def next_gid(self):
- for i in range(uid_minimum, uid_maximum):
- if not self.groups.has_key(i):
- return i
- def setup_home(uid, gid, path):
- if not os.path.exists(path):
- # Copy skeleton home dir
- os.system('/bin/cp -r %s "%s"' % ('/etc/skel', path))
- # Set a random face icon
- faces = glob.glob("/usr/share/*/apps/kdm/pics/users/*.png")
- if len(faces) > 0:
- facepath = os.path.join(path, '.face.icon')
- os.system('/bin/cp --remove-destination "%s" "%s"' % (random.choice(faces), facepath))
- os.chmod(facepath, 0644)
- # Set ownerships
- os.system('/bin/chown -R %d:%d "%s"' % (uid, gid, path))
- # Make sure at least top of the home dir's permissions are correct
- os.system('/bin/chown %d:%d "%s"' % (uid, gid, path))
- os.chmod(path, 0711)
- # methods
- def userList():
- def format(dict, uid):
- item = dict[uid]
- return (item.uid, item.name, item.realname)
- db = Database(for_read=True)
- return map(lambda x: format(db.users, x), db.users)
- def userInfo(uid):
- uid = int(uid)
- db = Database(for_read=True)
- if db.users.has_key(uid):
- u = db.users[uid]
- groups = []
- for item in db.groups.keys():
- if u.name in db.groups[item].members:
- groups.append(db.groups[item].name)
- grp = db.groups.get(u.gid, None)
- if grp:
- if grp.name in groups:
- groups.remove(grp.name)
- groups.insert(0, grp.name)
- ret = (
- u.name,
- u.realname,
- u.gid,
- u.homedir,
- u.shell,
- groups,
- )
- return ret
- else:
- fail(_(no_user_msg))
- def addUser(uid, name, realname, homedir, shell, password, groups, grants, blocks):
- if not realname:
- realname = ""
- if not homedir:
- homedir = "/home/" + name
- if not shell:
- shell = "/bin/bash"
- if not groups:
- groups = ["nogroup"]
- for item in groups:
- checkGroupName(item)
- checkName(name)
- checkRealName(realname)
- if password:
- checkPassword(password, (name, realname))
- db = Database()
- if uid == -1:
- uid = db.next_uid()
- else:
- try:
- uid = int(uid)
- if uid < 0 or uid > 65536:
- raise
- except:
- fail(_(invalid_userid_msg))
- if db.users.has_key(uid):
- fail(_(used_userid_msg))
- if db.users_by_name.has_key(name):
- fail(_(used_username_msg))
- # First group in the list is the user's main group
- g = db.groups_by_name.get(groups[0], None)
- if not g:
- fail(_(no_group_msg))
- gid = g.gid
- u = User()
- u.uid = uid
- u.gid = gid
- u.name = name
- u.realname = realname
- u.homedir = homedir
- u.shell = shell
- if password:
- u.password = shadowCrypt(password)
- else:
- u.password = "*"
- u.pwrest = [ "13094", "0", "99999", "7", "", "", "" ]
- db.users[uid] = u
- db.set_groups(name, groups)
- # No need to setup a real home dir for daemons
- if uid >= 1000 or homedir.startswith("/home/"):
- setup_home(uid, gid, homedir)
- setFace(uid, homedir)
- db.sync()
- for grant in grants:
- if grant != "":
- grantAuthorization(uid, grant)
- for block in blocks:
- if block != "":
- blockAuthorization(uid, block)
- return uid
- def setUser(uid, realname, homedir, shell, password, groups):
- uid = int(uid)
- db = Database()
- u = db.users.get(uid, None)
- if u:
- if realname:
- checkRealName(realname)
- u.realname = realname
- if homedir:
- u.homedir = homedir
- if shell:
- u.shell = shell
- if password:
- checkPassword(password, (u.name, u.realname, realname))
- u.password = shadowCrypt(password)
- if groups:
- # FIXME: check main group
- for item in groups:
- checkGroupName(item)
- db.set_groups(u.name, groups)
- db.sync()
- else:
- fail(_(no_user_msg))
- def deleteUser(uid, deletefiles):
- uid = int(uid)
- if uid == 0:
- fail(_(delete_root_msg))
- db = Database()
- u = db.users.get(uid, None)
- if u:
- #delete authorizations of user
- try:
- polkit.auth_revoke_all(uid)
- except:
- pass
- home = u.homedir[:]
- db.set_groups(u.name, [])
- del db.users[uid]
- db.sync()
- if deletefiles:
- os.system('/bin/rm -rf "%s"' % home)
- def groupList():
- def format(dict, gid):
- item = dict[gid]
- return (item.gid, item.name)
- db = Database(for_read=True)
- return map(lambda x: format(db.groups, x), db.groups)
- def addGroup(gid, name):
- checkGroupName(name)
- db = Database()
- if gid == -1:
- gid = db.next_gid()
- else:
- try:
- gid = int(gid)
- if gid < 0 or gid > 65536:
- raise
- except:
- fail(_(invalid_groupid_msg))
- if db.groups.has_key(gid):
- fail(_(used_groupid_msg))
- if db.groups_by_name.has_key(name):
- fail(_(used_groupname_msg))
- g = Group()
- g.gid = gid
- g.name = name
- g.members = []
- db.groups[gid] = g
- db.sync()
- return gid
- def deleteGroup(gid):
- gid = int(gid)
- db = Database()
- if db.groups.has_key(gid):
- del db.groups[gid]
- db.sync()
- #
- # Crypt function for shadow file
- #
- def shadowCrypt(password):
- des_salt = list('./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
- salt, magic = str(random.random())[-8:], '$1$'
- ctx = hashlib.md5(password)
- ctx.update(magic)
- ctx.update(salt)
- ctx1 = hashlib.md5(password)
- ctx1.update(salt)
- ctx1.update(password)
- final = ctx1.digest()
- for i in range(len(password), 0 , -16):
- if i > 16:
- ctx.update(final)
- else:
- ctx.update(final[:i])
- i = len(password)
- while i:
- if i & 1:
- ctx.update('\0')
- else:
- ctx.update(password[:1])
- i = i >> 1
- final = ctx.digest()
- for i in range(1000):
- ctx1 = hashlib.md5()
- if i & 1:
- ctx1.update(password)
- else:
- ctx1.update(final)
- if i % 3: ctx1.update(salt)
- if i % 7: ctx1.update(password)
- if i & 1:
- ctx1.update(final)
- else:
- ctx1.update(password)
- final = ctx1.digest()
- def _to64(v, n):
- r = ''
- while (n-1 >= 0):
- r = r + des_salt[v & 0x3F]
- v = v >> 6
- n = n - 1
- return r
- rv = magic + salt + '$'
- final = map(ord, final)
- l = (final[0] << 16) + (final[6] << 8) + final[12]
- rv = rv + _to64(l, 4)
- l = (final[1] << 16) + (final[7] << 8) + final[13]
- rv = rv + _to64(l, 4)
- l = (final[2] << 16) + (final[8] << 8) + final[14]
- rv = rv + _to64(l, 4)
- l = (final[3] << 16) + (final[9] << 8) + final[15]
- rv = rv + _to64(l, 4)
- l = (final[4] << 16) + (final[10] << 8) + final[5]
- rv = rv + _to64(l, 4)
- l = final[11]
- rv = rv + _to64(l, 2)
- return rv
- #
- # List authorizations by UID
- #
- def listUserAuthorizations(uid):
- actions = polkit.auth_list_uid(int(uid))
- auths = []
- for action in actions:
- action_info = polkit.action_info(action['action_id'])
- auths.append((action['action_id'], action['scope'], action_info['description'], action_info['policy_active'], action['negative']))
- return auths
- #
- # Grant authorization to user
- #
- def grantAuthorization(uid, action):
- uid = int(uid)
- if action == "*":
- for action_id in polkit.action_list():
- try:
- polkit.auth_revoke(uid, action_id)
- polkit.auth_add(action_id, polkit.SCOPE_ALWAYS, uid)
- except:
- return False
- else:
- try:
- polkit.auth_revoke(uid, action)
- polkit.auth_add(action, polkit.SCOPE_ALWAYS, uid)
- except:
- return False
- return True
- #
- # Revoke authorization of user
- #
- def revokeAuthorization(uid, action):
- uid = int(uid)
- if action == "*":
- for action_id in polkit.action_list():
- try:
- polkit.auth_revoke(uid, action_id)
- except:
- return False
- else:
- try:
- polkit.auth_revoke(uid, action)
- except:
- return False
- return True
- #
- # Block authorization of user
- #
- def blockAuthorization(uid, action):
- uid = int(uid)
- if action == "*":
- for action_id in polkit.action_list():
- try:
- polkit.auth_revoke(uid, action_id)
- polkit.auth_block(uid, action_id)
- except:
- return False
- else:
- try:
- polkit.auth_revoke(uid, action)
- polkit.auth_block(uid, action)
- except:
- return False
- return True
|