Files
core/system/base/baselayout/comar/usermgr.py
T
2015-02-24 11:18:35 +02:00

691 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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