You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-core/pym/core/server/bootstrap.py

502 lines
17 KiB

# -*- coding: utf-8 -*-
# Copyright 2012-2016 Mir Calculate. 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.
from . import cert_cmd
from . import post_request
import datetime
import subprocess
import shutil
from calculate.core.client.cert_func import new_key_req
from calculate.core.client.function import get_ip_mac_type
from calculate.core.datavars import DataVarsCore
from calculate.lib.utils.files import (makeDirectory, pathJoin, readFile,
writeFile, readFileEx)
from calculate.lib.utils.mount import isMount
from calculate.core.server.admin import Admins
import os
import hashlib
import pwd
import socket
import sys
import re
_ = lambda x: x
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_core3', sys.modules[__name__])
def parse_cert_date(date):
year = int(date[:4])
month = int(date[4:6])
day = int(date[6:8])
hour = int(date[8:10])
minute = int(date[10:12])
sec = int(date[12:14])
return datetime.datetime(year, month, day, hour, minute, sec)
def check(cert, key):
error_flag = 0
if not os.path.isfile(cert):
error_flag = 1
print(_('Certificate %s not found') % cert)
print(key, cert)
if not os.path.isfile(key):
error_flag = 1
print(_('Private key %s not found') % key)
if os.path.isfile(cert) and os.path.isfile(key):
import OpenSSL
# check correspondence certificate and private key
cmd_cert = 'openssl x509 -noout -modulus -in ' + cert
cmd_key = 'openssl rsa -noout -modulus -in ' + key
p_cert = subprocess.Popen(cmd_cert.split(), stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
p_key = subprocess.Popen(cmd_key.split(), stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
if not p_cert.stdout.read().strip() == p_key.stdout.read().strip():
print(_('The certificate does not match the private key'))
error_flag = 1
# check certificate date
cert_data = readFile(cert)
certobj = OpenSSL.crypto.load_certificate(
OpenSSL.SSL.FILETYPE_PEM, cert_data)
certobj.get_notBefore()
try:
not_after = parse_cert_date(certobj.get_notAfter())
not_before = parse_cert_date(certobj.get_notBefore())
date = datetime.datetime.now()
if not_before > date:
print(_('Certificate creation date later than current date'))
error_flag = 1
elif not_after < date:
print(_('Certificate expired'))
error_flag = 1
except ValueError:
print(_('Failed to get certificate work date'))
error_flag = 1
sys.exit(error_flag)
def init(cert, key, cert_path, data_path, certbase, args, port):
if args.remove_certificates:
key_force(cert_path, data_path)
new_serv_cert = False
if not check_serv_cert(cert_path):
print(_('Generating the server certificate'))
for step in range(2):
args = change_args(args, step)
create_server_cert(cert, key, cert_path, args, port)
new_serv_cert = True
else:
print(_('Server certificate now exists.'))
os.chmod(data_path, 0o700)
def force_user_cert(server_cert, cert_path, data_path, cert_base, user_name,
dv=None):
def is_crypthome_notmount(dv, username):
dv.Set('ur_login', user_name, force=True)
homedir = dv.Get('ur_home_path')
if (dv.GetBool('ur_home_crypt_set') and
'.Private' not in isMount(homedir)):
return True
return False
if not check_client_cert(user_name, server_cert=server_cert):
print(_('Generating the client certificate'))
else:
print(_('Regenerating the client certificate'))
group = "all"
if dv:
admins = Admins(dv)
if user_name not in admins:
admins[user_name] = group
admins.save()
group = admins[user_name]
if is_crypthome_notmount(dv, user_name):
print(_("User profile is encrypted. Please perform user login for "
"complete of certificate generation"))
return
create_client_cert(server_cert, cert_path, data_path, cert_base,
user_name, group)
def check_serv_cert(cert_path):
if os.path.isfile(os.path.join(cert_path, 'server.crt')) and \
os.path.isfile(os.path.join(cert_path, 'server.key')):
return True
return False
def check_client_cert(user_name, server_cert=None):
client_cert_path = check_user_path(user_name)
if server_cert:
server_host_name = get_certificate_dn(server_cert)
else:
server_host_name = socket.getfqdn()
crt_fn = os.path.join(client_cert_path, server_host_name + '.crt')
key_fn = os.path.join(client_cert_path, server_host_name + '.key')
if os.path.isfile(crt_fn) and os.path.isfile(key_fn):
return True
return False
def change_args(args, step=None):
if step == 0:
args.host = False
args.gen_root_cert = True
args.root_host = False
args.use_root_cert = False
elif step == 1:
args.gen_root_cert = False
args.use_root_cert = True
return args
def create_server_cert(cert, key, cert_path, args, port):
cert_cmd.check_server_certificate(cert, key, cert_path, args, port,
auto=True)
def create_client_cert(server_cert, cert_path, data_path, certbase, user_name,
group="all"):
client_cert_path = check_user_path(user_name)
if not client_cert_path:
print(_('no path to the client certificate'))
return 1
req_id = create_request(server_cert, cert_path, data_path, certbase,
client_cert_path, user_name)
sign_certificate(req_id, cert_path, data_path, group)
get_certificate(cert_path, data_path, certbase, client_cert_path, user_name,
server_cert=server_cert)
def check_user_path(user_name):
try:
pwdObj = pwd.getpwnam(user_name)
except KeyError as e:
print(e)
return None
home_dir = pwdObj.pw_dir
if not os.path.isdir(home_dir):
if not makeDirectory(home_dir):
return None
os.chown(home_dir, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(home_dir, 0o700)
calc_dir = os.path.join(home_dir, '.calculate')
cert_dir = os.path.join(calc_dir, 'client_cert')
for directory in [calc_dir, cert_dir]:
if not os.path.isdir(directory):
if not makeDirectory(directory):
return None
os.chown(directory, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(directory, 0o755)
for path in os.walk(cert_dir):
os.chown(path[0], pwdObj.pw_uid, pwdObj.pw_gid)
for _file in path[2]:
fn = pathJoin(path[0], _file)
if os.path.isfile(fn):
os.chown(fn, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(fn, 0o644)
return cert_dir
def create_request(server_cert, cert_path, data_path, certbase,
client_cert_path, user_name):
server_host_name = get_certificate_dn(server_cert)
key = os.path.join(client_cert_path, server_host_name + '.key')
client_req_file = new_key_req(key, client_cert_path, server_host_name,
auto=True)
try:
pwdObj = pwd.getpwnam(user_name)
except KeyError as e:
print(e)
return None
for files in [client_req_file, key + '_pub']:
if os.path.exists(files):
os.chown(files, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(files, 0o644)
if os.path.exists(key):
os.chown(key, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(key, 0o600)
ip, mac, client_type = get_ip_mac_type()
data = readFile(client_req_file)
req_id = post_request.serv_post_client_request(
data, data_path, ip, mac, client_type, certbase, cert_path)
fc = open(os.path.join(client_cert_path, 'req_id'), 'w')
fc.write(req_id)
fc.close()
return req_id
def sign_certificate(req_id, cert_path, data_path, group="all"):
cert_cmd.sing_req_by_server(req_id, cert_path, data_path, auto=True,
group_name=group)
def get_certificate_dn(cert_file):
cert_data = readFile(cert_file)
if cert_data:
import OpenSSL
certobj = OpenSSL.crypto.load_certificate(
OpenSSL.SSL.FILETYPE_PEM, cert_data)
cert_info = dict(certobj.get_subject().get_components())
return cert_info["CN"]
return "localhost"
def clear_localuser_certificates(certbase):
"""
Удалить все пользовательские сертификаты, создаваемые для локальных
пользователей
"""
certdata = readFileEx(certbase, grab=True)
certdn = os.path.dirname(certbase)
# оставляем только сертификаты, которые не содержат отметки
# для какого локального пользователя они созданы
writedata = "\n".join(x[0] for x in re.findall("^((\S+\s+){6}\S+)\s*$",
certdata, flags=re.M))
with writeFile(certbase) as f:
f.write("%s\n"%writedata)
# удаляем физически сертификаты, созданные для локальных пользователей
for localcert in re.finditer("^(\S+)\s+(\S+\s+){6}\S+\s*$",
certdata, flags=re.M):
cert_fn = "%s/%s.crt"%(certdn, localcert.group(1))
try:
os.unlink(cert_fn)
except OSError:
print(_("Failed to remove local client certificate") % cert_fn)
def get_certificate(cert_path, data_path, certbase, client_cert_path,
user_name, server_cert=None):
req_id_file = os.path.join(client_cert_path, 'req_id')
if not os.path.exists(req_id_file):
print(_("request not sent or file %s deleted") % req_id_file)
return 1
fc = open(req_id_file, 'r')
req_id = fc.read()
fc.close()
if server_cert:
server_host_name = get_certificate_dn(server_cert)
else:
server_host_name = socket.getfqdn()
req_file = os.path.join(client_cert_path, server_host_name + '.csr')
if not os.path.exists(req_file):
print(_('Request %s not found') % req_file)
return 1
request = readFile(req_file)
md5 = hashlib.md5()
md5.update(request.encode("UTF-8"))
md5sum = md5.hexdigest()
result = post_request.serv_get_client_cert(
req_id, md5sum, data_path, certbase, cert_path,
localuser=user_name)
cert = result[0]
if len(result) > 1:
ca_root = result[1]
else:
return None
if cert == '1':
print(_('The signature request was rejected!'))
return 1
elif cert == '2':
print(_("The signature request has not been examined yet."))
print(_("Your request ID = %s") % req_id)
return 1
elif cert == '3':
print(_("The signature request does not match earlier data."))
return 1
elif cert == '4':
print(_("The request was sent from another IP."))
return 1
cert_file = os.path.join(client_cert_path, server_host_name + '.crt')
fc = open(cert_file, 'w')
fc.write(cert)
fc.close()
try:
pwdObj = pwd.getpwnam(user_name)
except KeyError as e:
print(e)
return None
os.chown(cert_file, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(cert_file, 0o600)
os.unlink(req_id_file)
print(_('Certificate saved. Your certificate ID: %s') % req_id)
if ca_root:
clVars = DataVarsCore()
clVars.importCore()
clVars.flIniFile()
system_ca_db = clVars.Get('cl_glob_root_cert')
if os.path.exists(system_ca_db):
if ca_root in readFile(system_ca_db):
return 0
ca_dir = os.path.join(client_cert_path, 'ca')
if not os.path.isdir(ca_dir):
os.makedirs(ca_dir)
os.chown(ca_dir, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(ca_dir, 0o755)
root_cert_md5 = os.path.join(ca_dir, "cert_list")
md5 = hashlib.md5()
md5.update(ca_root.encode("UTF-8"))
md5sum = md5.hexdigest()
if not os.path.exists(root_cert_md5):
fc = open(root_cert_md5, "w")
fc.close()
filename = None
with open(root_cert_md5) as fd:
t = fd.read()
# for each line
for line in t.splitlines():
# Split string into a words list
words = line.split(' ', 1)
if words[0] == md5sum:
filename = words[1]
if not filename:
import OpenSSL
certobj = OpenSSL.crypto.load_certificate(
OpenSSL.SSL.FILETYPE_PEM, ca_root)
issuer = certobj.get_issuer().get_components()
for item in issuer:
if item[0] == 'CN':
filename = item[1]
fc = open(root_cert_md5, "a")
fc.write('%s %s\n' % (md5sum, filename))
fc.close()
os.chown(root_cert_md5, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(root_cert_md5, 0o644)
if not filename:
print(_('Field "CN" not found in the certificate!'))
return 1
ca_cert = os.path.join(ca_dir, filename)
fd = open(ca_cert, 'w')
fd.write(ca_root)
fd.close()
os.chown(ca_cert, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(ca_cert, 0o644)
user_root_cert = os.path.join(ca_dir, 'ca_root.crt')
fa = open(user_root_cert, 'a')
fa.write(ca_root)
fa.close()
os.chown(user_root_cert, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(user_root_cert, 0o644)
# print _("Certificate added")
# else:
# print _("file with the CA certificate now exists")
trust_dir = os.path.join(client_cert_path, 'trusted')
if not os.path.isdir(trust_dir):
os.makedirs(trust_dir)
os.chown(trust_dir, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(trust_dir, 0o755)
ca_certs = os.path.join(trust_dir, "cert.list")
if not os.path.exists(ca_certs):
fc = open(ca_certs, "w")
fc.close()
os.chown(ca_certs, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(ca_certs, 0o644)
host = 'localhost'
filename = host
cert_file_trust = os.path.join(trust_dir, filename)
fc = open(cert_file_trust, "w")
fc.write(ca_root)
fc.close()
os.chown(cert_file_trust, pwdObj.pw_uid, pwdObj.pw_gid)
os.chmod(cert_file_trust, 0o644)
with open(ca_certs) as fd:
t = fd.read()
# for each line
for line in t.splitlines():
# Split string into a words list
words = line.split()
if len(words) > 1:
# if first word...
if words[0] == host:
return 0
# Open file with compliance server certificates and server hostname
fcl = open(ca_certs, "a")
fcl.write(host + ' ' + filename + '\n')
fcl.close()
return 0
def key_force(cert_path, data_path):
while True:
try:
resp = input(_('Do you really want to remove all '
'certificates, requests and config files from '
'the server?') + ' (yes/no): ')
except KeyboardInterrupt:
resp = 'no'
if resp.lower() in ['n', 'no']:
return 0
elif resp.lower() in ['y', 'yes']:
break
if os.path.isdir(cert_path):
shutil.rmtree(cert_path)
remove_dirs = ['conf', 'server_certs', 'client_certs', 'pids', 'sids']
for rm_dir in remove_dirs:
remove_dir = os.path.join(data_path, rm_dir)
if os.path.isdir(remove_dir):
shutil.rmtree(remove_dir)
remove_files = ['sid.db', 'sid_pid']
for rm_file in remove_files:
remove_file = os.path.join(data_path, rm_file)
if os.path.isfile(remove_file):
os.unlink(remove_file)