#-*- coding: utf-8 -*- # Copyright 2008-2010 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import os import hashlib import crypt import string import time from random import choice from base64 import encodestring as _b64enc b64encode = lambda x: _b64enc(x).rstrip() try: from smbpasswd import lmhash,nthash except ImportError: lmhash,nthash = None,None from cl_print import color_print # для создания сертификата import pwd from server.utils import execProg import cl_overriding import cl_lang from cl_utils import genpassword, removeDir # Перевод модуля tr = cl_lang.lang() tr.setLocalDomain('cl_lib') tr.setLanguage(sys.modules[__name__]) class encrypt(color_print): """Класс хранения общих методов используемых для настройки сервисов Методы шифрования, создания сертификатов и.т. д """ def __GenCryptSalt__(self, len=2): """Генерация соли для хеширования пароля (CRYPT)""" chars = string.letters + string.digits + "./" salt = "" for i in range(len): salt = salt + choice(chars) return salt def getHashPasswd(self, password, SecHashAlg): """Генерация хеша пароля, Поддерживаемые алгоритмы шифрования пароля: plain, md5, smd5, crypt, sha, ssha, lm, nt, shadow_ssha512, shadow_ssha256, shadow_md5 """ if not password: self.printERROR(_("ERROR") + " getHashPasswd: " + _("empty password")) return False hashPwd = "" if SecHashAlg == "plain": hashPwd = password elif SecHashAlg == "md5": h = hashlib.md5(password) hashPwd = "{MD5}" + b64encode(h.digest()) elif SecHashAlg == "smd5": salt = os.urandom(4) h = hashlib.md5(password) h.update(salt) hashPwd = "{SMD5}" + b64encode(h.digest() + salt) elif SecHashAlg == "shadow_ssha512": salt = self.__GenCryptSalt__(8) hashPwd = crypt.crypt(password, "$6$%s$"%salt) elif SecHashAlg == "shadow_ssha256": salt = self.__GenCryptSalt__(8) hashPwd = crypt.crypt(password, "$5$%s$"%salt) elif SecHashAlg == "shadow_md5": salt = self.__GenCryptSalt__(8) hashPwd = crypt.crypt(password, "$1$%s$"%salt) elif SecHashAlg == "crypt": salt = self.__GenCryptSalt__() hashPwd = "{CRYPT}" + crypt.crypt(password, salt) elif SecHashAlg == "sha": h = hashlib.sha1(password) hashPwd = "{SHA}" + b64encode(h.digest()) elif SecHashAlg == "ssha": salt = os.urandom(4) h = hashlib.sha1(password) h.update(salt) hashPwd = "{SSHA}" + b64encode(h.digest() + salt) elif SecHashAlg == "lm" and lmhash: hashPwd = lmhash(password) elif SecHashAlg == "nt" and nthash: hashPwd = nthash(password) else: if SecHashAlg in ("lm","nt"): self.printERROR(_("ERROR") + " getHashPasswd: " + (_("Failed to support '%s' crypto algorithm") %SecHashAlg) + " " + _("without py-smbpasswd")) else: self.printERROR(_("ERROR") + " getHashPasswd: " + _("Failed to support '%s' crypto algorithm") %SecHashAlg) return False return hashPwd class certificate(color_print): sslCountry = "RU" sslState = "Saint-Petersburg" sslLocality = "Saint-Petersburg" sslOrganization = "Calculate Ltd." sslUnit = "SSL" sslCommonName = "localhost" sslEmail = "support@calculate.ru" nsCertType = "server" sslDays = 3653 sslBits = 1024 userName = "root" fileMode = 0400 certsDir = "/var/calculate/ssl" tmpDir = os.path.join(certsDir,"tmp") certFile = os.path.join(tmpDir, "server.crt") keyFile = os.path.join(tmpDir, "server.key") csrFile = os.path.join(tmpDir, "server.csr") CAPath = os.path.join(certsDir,"main") CACertFileName = "CA.crt" CAKeyFileName = "CA.key" CACrlFileName = "CA.crl" rCACertPath = "crt" rCAKeyPath = "key" rCACrlPath = "crl" rSerialFileName = "serial" rDatabaseFileName = "index.dat" sslFile = "/usr/bin/openssl" templCnfCA = """[ ca ] default_ca = CA_default [ CA_default ] dir = %(CAPath)s certs = $dir/%(rCACertPath)s crl_dir = $dir/%(rCACrlPath)s database = $dir/%(rDatabaseFileName)s new_certs_dir = $dir/%(rCACertPath)s certificate = $dir/%(rCACertFile)s serial = $dir/%(rSerialFileName)s crlnumber = $dir/crlnumber crl = $dir/%(rCACrlFile)s private_key = $dir/%(rCAKeyFile)s RANDFILE = $dir/%(rRandFile)s x509_extensions = usr_cert name_opt = ca_default cert_opt = ca_default default_days = 365 default_crl_days= 30 default_md = default preserve = no policy = policy_match [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ usr_cert ] basicConstraints=CA:FALSE subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer [ req ] prompt = no default_bits = %(sslBits)s x509_extensions = v3_ca string_mask = utf8only distinguished_name = req_dn [ req_dn ] C = %(sslCountry)s ST = %(sslState)s L = %(sslLocality)s O = %(sslOrganization)s OU = %(sslUnit)s CN = %(sslCommonName)s emailAddress = %(sslEmail)s [ cert_type ] nsCertType = %(nsCertType)s [ v3_ca ] subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer basicConstraints = CA:true """ templCnfData = """[ req ] prompt = no default_bits = %(sslBits)s distinguished_name = req_dn [ req_dn ] C = %(sslCountry)s ST = %(sslState)s L = %(sslLocality)s O = %(sslOrganization)s OU = %(sslUnit)s CN = %(sslCommonName)s emailAddress = %(sslEmail)s [ cert_type ] nsCertType = %(nsCertType)s """ templCreateKey = "%(sslFile)s genrsa -out '%(certKeyFile)s' %(sslBits)s" templCreateCert = "%(sslFile)s req -new -x509 -days %(sslDays)s "\ "-config '%(cnfFile)s' -key '%(certKeyFile)s' "\ "-out '%(certFile)s'" templCreateReq = "%(sslFile)s req -new -days %(sslDays)s "\ "-config '%(cnfFile)s' -key '%(certKeyFile)s' "\ "-out '%(certCsrFile)s'" templCreateSignCert = "%(sslFile)s ca -batch -config '%(cnfFile)s' "\ "-policy policy_anything -days '%(sslDays)s' "\ "-out '%(certFile)s' -infiles '%(certCsrFile)s'" def createCnfFile(self, textCnf): '''Generate openssl.cnf file''' if not os.path.exists(self.sslFile): self.printERROR(_("%s not found")%self.sslFile) return False strData = time.strftime("%Y%m%d%H%M%S",time.localtime(time.time())) cnfFileName = "%s.%s.cnf" %(strData,genpassword()) if not os.path.exists(self.tmpDir): self._createDir(self.tmpDir) cnfFile = os.path.join(self.tmpDir,cnfFileName) self._createFile(cnfFile, textCnf) return cnfFile def checkCertificate(self, certFile): # Проверка сертификата textLine = execProg("%s x509 -subject -fingerprint -noout -in '%s'" %(self.sslFile, certFile)) if textLine is False: self.printERROR(_("Error checking certificate %s")%certFile) return False return True def createCertificateAutority(self,sslCountry=sslCountry, sslState=sslState, sslLocality=sslLocality, sslOrganization=sslOrganization, sslUnit=sslUnit, sslCommonName=sslCommonName, sslEmail=sslEmail, nsCertType=nsCertType, sslDays=sslDays, sslBits=sslBits, userName=userName, CAPath=CAPath, CACertFileName=CACertFileName, CAKeyFileName=CAKeyFileName, CACrlFileName=CACrlFileName, fileMode=fileMode, force=False): '''Create CA''' rCACertFile = os.path.join(self.rCACertPath, CACertFileName) rCAKeyFile = os.path.join(self.rCAKeyPath, CAKeyFileName) rCACrlFile = os.path.join(self.rCACrlPath, CACrlFileName) rRandFile = os.path.join(self.rCAKeyPath,".rnd") CACertFile = os.path.join(CAPath, rCACertFile) CAKeyFile = os.path.join(CAPath, rCAKeyFile) # Cerificates exists if not force and os.path.isfile(CACertFile) and\ os.path.isfile(CAKeyFile): return True # получаем id и gid пользователя try: pwdObj = pwd.getpwnam(userName) except: self.printERROR(_("User %s not found")%userName) return False uid = pwdObj.pw_uid gid = pwdObj.pw_gid # delete certificate dir if os.path.isdir(CAPath): removeDir(CAPath) # create certificate dirs self._createDir(CAPath) CACertPath = os.path.join(CAPath, self.rCACertPath) CAKeyPath = os.path.join(CAPath, self.rCAKeyPath) CACrlPath = os.path.join(CAPath, self.rCACrlPath) for createDir in [CACertPath, CAKeyPath, CACrlPath]: self._createDir(createDir) # save serial number SerialFile = os.path.join(CAPath, self.rSerialFileName) self._createFile(SerialFile, "01\n") # create database file DatabaseFile = os.path.join(CAPath, self.rDatabaseFileName) self._createFile(DatabaseFile, "") textCnf = self.templCnfCA%{'CAPath':CAPath, 'rCACertPath':self.rCACertPath, 'rCACrlPath':self.rCACrlPath, 'rDatabaseFileName':self.rDatabaseFileName, 'rCACertFile':rCACertFile, 'rSerialFileName':self.rSerialFileName, 'rCACrlFile':rCACrlFile, 'rCAKeyFile':rCAKeyFile, 'rRandFile':rRandFile, 'sslBits':sslBits, 'sslCountry':sslCountry, 'sslState':sslState, 'sslLocality':sslLocality, 'sslOrganization':sslOrganization, 'sslUnit':sslUnit, 'sslCommonName':sslCommonName, 'sslEmail':sslEmail, 'nsCertType':nsCertType} cnfFile = self.createCnfFile(textCnf) if cnfFile is False: return False # generate CA RSA key execStr = self.templCreateKey%{'sslFile':self.sslFile, 'certKeyFile':CAKeyFile, 'sslBits':sslBits} if execProg(execStr) is False: self.printERROR(_("Can not execute '%s'")%execStr) return False if os.path.exists(CAKeyFile): os.chown(CAKeyFile, uid,gid) os.chmod(CAKeyFile, fileMode) # create CA execStr = self.templCreateCert%{'sslFile':self.sslFile, 'sslDays':sslDays, 'cnfFile':cnfFile, 'certKeyFile':CAKeyFile, 'certFile':CACertFile} if execProg(execStr) is False: self.printERROR(_("Can not execute '%s'")%execStr) return False if os.path.exists(CACertFile): os.chown(CACertFile, uid,gid) os.chmod(CACertFile, fileMode) if os.path.exists(cnfFile): os.remove(cnfFile) # check certificate return self.checkCertificate(CACertFile) def createSignedCertificate(self, sslCountry=sslCountry, sslState=sslState, sslLocality=sslLocality, sslOrganization=sslOrganization, sslUnit=sslUnit, sslCommonName=sslCommonName, sslEmail=sslEmail, nsCertType=nsCertType, sslDays=sslDays, sslBits=sslBits, userName=userName, CAPath=CAPath, CACertFileName=CACertFileName, CAKeyFileName=CAKeyFileName, CACrlFileName=CACrlFileName, certFile=certFile, fileMode=fileMode, keyFile=keyFile, csrFile=csrFile, force=False): '''Create signed CA certificate''' certAndKeyFiles = [certFile, keyFile, csrFile] foundCertFiles = map(lambda x: os.path.exists(x), certAndKeyFiles) if not force and foundCertFiles[0] and foundCertFiles[1]: return True foundCertFiles = map(lambda x: x[1], filter(lambda x: x[0], zip(foundCertFiles, certAndKeyFiles))) # Удаляем файлы map(lambda x: os.remove(x), foundCertFiles) # получаем id и gid пользователя try: pwdObj = pwd.getpwnam(userName) except: self.printERROR(_("User %s not found")%userName) return False uid = pwdObj.pw_uid gid = pwdObj.pw_gid # create dirs for fileName in certAndKeyFiles: dirName = os.path.split(fileName)[0] if not os.path.exists(dirName): self._createDir(dirName, uid=uid, gid=gid) rCACertFile = os.path.join(self.rCACertPath, CACertFileName) rCAKeyFile = os.path.join(self.rCAKeyPath, CAKeyFileName) rCACrlFile = os.path.join(self.rCACrlPath, CACrlFileName) rRandFile = os.path.join(self.rCAKeyPath,".rnd") textCnf = self.templCnfCA%{'CAPath':CAPath, 'rCACertPath':self.rCACertPath, 'rCACrlPath':self.rCACrlPath, 'rDatabaseFileName':self.rDatabaseFileName, 'rCACertFile':rCACertFile, 'rSerialFileName':self.rSerialFileName, 'rCACrlFile':rCACrlFile, 'rCAKeyFile':rCAKeyFile, 'rRandFile':rRandFile, 'sslBits':sslBits, 'sslCountry':sslCountry, 'sslState':sslState, 'sslLocality':sslLocality, 'sslOrganization':sslOrganization, 'sslUnit':sslUnit, 'sslCommonName':sslCommonName, 'sslEmail':sslEmail, 'nsCertType':nsCertType} cnfFile = self.createCnfFile(textCnf) if cnfFile is False: return False # generate RSA key execStr = self.templCreateKey%{'sslFile':self.sslFile, 'certKeyFile':keyFile, 'sslBits':sslBits} if execProg(execStr) is False: self.printERROR(_("Can not execute '%s'")%execStr) return False if os.path.exists(keyFile): os.chown(keyFile, uid,gid) os.chmod(keyFile, fileMode) # generate request execStr = self.templCreateReq%{'sslFile':self.sslFile, 'sslDays':sslDays, 'cnfFile':cnfFile, 'certKeyFile':keyFile, 'certCsrFile':csrFile} if execProg(execStr) is False: self.printERROR(_("Can not execute '%s'")%execStr) return False if os.path.exists(csrFile): os.chown(csrFile, uid,gid) os.chmod(csrFile, fileMode) # set database attribute databaseAttrFileName = os.path.join(CAPath, "index.dat.attr") self._createFile(databaseAttrFileName, "unique_subject = no\n") # generate signed cerificate execStr = self.templCreateSignCert%{'sslFile':self.sslFile, 'sslDays':sslDays, 'cnfFile':cnfFile, 'certFile':certFile, 'certCsrFile':csrFile} if execProg(execStr) is False: self.printERROR(_("Can not execute '%s'")%execStr) return False if os.path.exists(certFile): os.chown(certFile, uid,gid) os.chmod(certFile, fileMode) if os.path.exists(cnfFile): os.remove(cnfFile) # check certificate return self.checkCertificate(certFile) def createCertificate(self, sslCountry=sslCountry, sslState=sslCountry, sslLocality=sslLocality, sslOrganization=sslOrganization, sslUnit=sslUnit, sslCommonName=sslCommonName, sslEmail=sslEmail, nsCertType=nsCertType, sslDays=sslDays, sslBits=sslBits, userName=userName, certFile=certFile, fileMode=fileMode, keyFile=keyFile): """Создает сертификат""" certAndKeyFiles = [certFile, keyFile] foundCertFiles = filter(lambda x: os.path.exists(x), certAndKeyFiles) if len(foundCertFiles)==2: return True # Удаляем файл сертификата map(lambda x: os.remove(x), foundCertFiles) # получаем id и gid пользователя try: pwdObj = pwd.getpwnam(userName) except: self.printERROR(_("User %s not found")%userName) return False uid = pwdObj.pw_uid gid = pwdObj.pw_gid textCnf=self.templCnfData%{'sslBits':sslBits, 'sslCountry':sslCountry, 'sslState':sslState, 'sslLocality':sslLocality, 'sslOrganization':sslOrganization, 'sslUnit':sslUnit, 'sslCommonName':sslCommonName, 'sslEmail':sslEmail, 'nsCertType':nsCertType} cnfFile = self.createCnfFile(textCnf) if cnfFile is False: return False # Cоздание директорий for fileName in certAndKeyFiles: dirName = os.path.split(fileName)[0] if not os.path.exists(dirName): self._createDir(dirName, uid=uid, gid=gid) # Создание сертификата textLine = execProg("%s req -new -x509 -nodes -config '%s'" "-days %s -out '%s'-keyout '%s'" %(self.sslFile, cnfFile, sslDays, certFile, keyFile)) if textLine is False: self.printERROR(_("Failed to create certificate %s")%certFile) return False # Удаление конфигурационного файла if os.path.exists(cnfFile): os.remove(cnfFile) # Меняем права if os.path.exists(certFile): os.chown(certFile, uid,gid) os.chmod(certFile, fileMode) if os.path.exists(keyFile): os.chown(keyFile, uid,gid) os.chmod(keyFile, fileMode) return self.checkCertificate(certFile) def _createDir(self, dirName, uid=0, gid=0, mode=0700): """Создание пользовательской директории""" if not os.path.exists(dirName): os.makedirs(dirName) if mode: os.chmod(dirName,mode) os.chown(dirName,uid,gid) return True else: self.printERROR(_("Path %s exists") %dirName) return False def _createFile(self, fileName, fileTxt, uid=0, gid=0, mode=0600): """Создает пользовательский файл с содержимым Если директория файла не существует то ошибка """ dirName = os.path.split(fileName)[0] if not os.path.exists(dirName): self.printERROR(_("Path %s does not exist") %dirName) return False fd = os.open(fileName, os.O_CREAT) os.close(fd) os.chmod(fileName, 0600) os.chown(fileName,uid,gid) if fileTxt: FD = open(fileName, "r+") FD.write(fileTxt) FD.close() os.chmod(fileName, mode) return True def getHash(password, encr): """Получить хеш пароля password - пароль encr - алгоритм шифрования, например 'ssha' """ encryptObj = encrypt() hashPwd = encryptObj.getHashPasswd(password, encr.lower()) if hashPwd: return hashPwd else: encryptObj.printERROR(_("Error encrypt password, " "method getHash()")) cl_overriding.exit(1)