def _lookup(self, order_name):
        """ sohw order details based on ordername """
        self.logger.debug('Order._lookup({0})'.format(order_name))
        order_dic = {}

        tmp_dic = self._info(order_name)
        if tmp_dic:
            if 'status' in tmp_dic:
                order_dic['status'] = tmp_dic['status']
            if 'expires' in tmp_dic:
                order_dic['expires'] = uts_to_date_utc(tmp_dic['expires'])
            if 'notbefore' in tmp_dic:
                if tmp_dic['notbefore'] != 0:
                    order_dic['notBefore'] = uts_to_date_utc(
                        tmp_dic['notbefore'])
            if 'notafter' in tmp_dic:
                if tmp_dic['notafter'] != 0:
                    order_dic['notAfter'] = uts_to_date_utc(
                        tmp_dic['notafter'])
            if 'identifiers' in tmp_dic:
                try:
                    order_dic['identifiers'] = json.loads(
                        tmp_dic['identifiers'])
                except BaseException:
                    self.logger.error(
                        'Order.lookup(): error while parsing the identifier {0}'
                        .format(tmp_dic['identifiers']))
            try:
                authz_list = self.dbstore.authorization_lookup(
                    'order__name', order_name, ['name', 'status__name'])
            except BaseException as err_:
                self.logger.critical(
                    'acme2certifier database error in Order._lookup(): {0}'.
                    format(err_))
                authz_list = []
            if authz_list:
                order_dic["authorizations"] = []
                # collect status of different authorizations in list
                validity_list = []
                for authz in authz_list:
                    if 'name' in authz:
                        order_dic["authorizations"].append('{0}{1}{2}'.format(
                            self.server_name, self.path_dic['authz_path'],
                            authz['name']))
                    if 'status__name' in authz:
                        if authz['status__name'] == 'valid':
                            validity_list.append(True)
                        else:
                            validity_list.append(False)

                # update orders status from pending to ready
                if validity_list and 'status' in order_dic:
                    if False not in validity_list and order_dic[
                            'status'] == 'pending':
                        self._update({'name': order_name, 'status': 'ready'})

        self.logger.debug('Order._lookup() ended')
        return order_dic
Exemple #2
0
    def _convert_data(self, cert_list):
        """ convert data from uts to real date """
        self.logger.debug('Housekeeping._convert_dates()')
        for cert in cert_list:
            expire_list = ('order.expires', 'authorization.expires',
                           'challenge.expires')
            for ele in expire_list:
                if ele in cert and cert[ele]:
                    cert[ele] = uts_to_date_utc(cert[ele], '%Y-%m-%d %H:%M:%S')

            # set uts to 0 if we do not have them in dictionary
            if 'certificate.issue_uts' not in cert or 'certificate.expire_uts' not in cert:
                cert['certificate.issue_uts'] = 0
                cert['certificate.expire_uts'] = 0

            # if uts is zero we try to get the dates from certificate
            if cert['certificate.issue_uts'] == 0 or cert[
                    'certificate.expire_uts'] == 0:
                # cover cases without certificate in dict
                if 'certificate.cert_raw' in cert:
                    (issue_uts,
                     expire_uts) = cert_dates_get(self.logger,
                                                  cert['certificate.cert_raw'])
                    cert['certificate.issue_uts'] = issue_uts
                    cert['certificate.expire_uts'] = expire_uts
                else:
                    cert['certificate.issue_uts'] = 0
                    cert['certificate.expire_uts'] = 0

            if cert['certificate.issue_uts'] > 0 and cert[
                    'certificate.expire_uts'] > 0:
                cert['certificate.issue_date'] = uts_to_date_utc(
                    cert['certificate.issue_uts'], '%Y-%m-%d %H:%M:%S')
                cert['certificate.expire_date'] = uts_to_date_utc(
                    cert['certificate.expire_uts'], '%Y-%m-%d %H:%M:%S')
            else:
                cert['certificate.issue_date'] = ''
                cert['certificate.expire_date'] = ''

        # add serial number
            if 'certificate.cert_raw' in cert:
                try:
                    cert['certificate.serial'] = cert_serial_get(
                        self.logger, cert['certificate.cert_raw'])
                except BaseException:
                    cert['certificate.serial'] = ''

        return cert_list
Exemple #3
0
    def _info(self, challenge_name):
        """ get challenge details """
        self.logger.debug('Challenge._info({0})'.format(challenge_name))
        try:
            challenge_dic = self.dbstore.challenge_lookup(
                'name',
                challenge_name,
                vlist=('type', 'token', 'status__name', 'validated'))
        except BaseException as err_:
            self.logger.critical(
                'acme2certifier database error in Challenge._info(): {0}'.
                format(err_))
            challenge_dic = {}

        if 'status' in challenge_dic and challenge_dic['status'] == 'valid':
            if 'validated' in challenge_dic:
                # convert validated timestamp to RFC3339 format - if it fails remove key from dictionary
                try:
                    challenge_dic['validated'] = uts_to_date_utc(
                        challenge_dic['validated'])
                except BaseException:
                    challenge_dic.pop('validated')
        else:
            if 'validated' in challenge_dic:
                challenge_dic.pop('validated')

        self.logger.debug('Challenge._info({0}) ended'.format(challenge_name))
        return challenge_dic
    def _store_cert(self, ca_id, cert_name, serial, cert, name_hash,
                    issuer_hash):
        """ store certificate to database """
        self.logger.debug('CAhandler._store_cert()')

        # insert certificate into item table
        insert_date = uts_to_date_utc(uts_now(), '%Y%m%d%H%M%SZ')
        item_dic = {
            'type': 3,
            'comment': 'from acme2certifier',
            'source': 2,
            'date': insert_date,
            'name': cert_name
        }
        row_id = self._item_insert(item_dic)
        # insert certificate to cert table
        cert_dic = {
            'item': row_id,
            'serial': serial,
            'issuer': ca_id,
            'ca': 0,
            'cert': cert,
            'iss_hash': issuer_hash,
            'hash': name_hash
        }
        _row_id = self._cert_insert(
            cert_dic)  # lgtm [py/unused-local-variable]

        self.logger.debug('CAhandler._store_cert() ended')
    def _csr_import(self, csr, request_name):
        """ check existance of csr and load into db """
        self.logger.debug('CAhandler._csr_insert()')

        csr_info = self._csr_search('request', csr)

        if not csr_info:

            # csr does not exist in db - lets import it
            insert_date = uts_to_date_utc(uts_now(), '%Y%m%d%H%M%SZ')
            item_dic = {
                'type': 2,
                'comment': 'from acme2certifier',
                'source': 2,
                'date': insert_date,
                'name': request_name
            }
            row_id = self._item_insert(item_dic)

            # insert csr
            csr_info = {'item': row_id, 'signed': 1, 'request': csr}
            self._csr_insert(csr_info)

        self.logger.debug('CAhandler._csr_insert()')
        return csr_info
    def revoke(self,
               cert,
               rev_reason='unspecified',
               rev_date=uts_to_date_utc(uts_now())):
        """ revoke certificate """
        self.logger.debug('CAhandler.revoke({0}: {1})'.format(
            rev_reason, rev_date))
        # lookup REST-PATH of issuing CA
        ca_dic = self._ca_get_properties('name', self.ca_name)
        if 'href' in ca_dic:
            # get serial from pem file
            serial = cert_serial_get(self.logger, cert)
            if serial:
                # get certificate information via rest by search for ca+ serial
                cert_dic = self._cert_get_properties(serial, ca_dic['href'])
                if 'certificates' in cert_dic:
                    if len(cert_dic['certificates']
                           ) > 0 and 'href' in cert_dic['certificates'][0]:
                        # revoke the cert
                        data = {
                            'newStatus': 'revoked',
                            'crlReason': rev_reason,
                            'invalidityDate': rev_date
                        }
                        cert_dic = self._api_post(
                            cert_dic['certificates'][0]['href'] + '/status',
                            data)
                        if 'status' in cert_dic:
                            # code = cert_dic['status']
                            code = 400
                            message = 'urn:ietf:params:acme:error:alreadyRevoked'
                            if 'message' in cert_dic:
                                detail = cert_dic['message']
                            else:
                                detail = 'no details'
                        else:
                            code = 200
                            message = None
                            detail = None
                    else:
                        code = 404
                        message = 'urn:ietf:params:acme:error:serverInternal'
                        detail = 'Cert path could not be found'
                else:
                    code = 404
                    message = 'urn:ietf:params:acme:error:serverInternal'
                    detail = 'Cert could not be found'
            else:
                code = 404
                message = 'urn:ietf:params:acme:error:serverInternal'
                detail = 'failed to get serial number from cert'
        else:
            code = 404
            message = 'urn:ietf:params:acme:error:serverInternal'
            detail = 'CA could not be found'

        return (code, message, detail)
    def revoke(self, cert, rev_reason='unspecified', rev_date=None):
        """ revoke certificate """
        self.logger.debug('CAhandler.revoke()')

        # overwrite revocation date - we ignore what has been submitted
        rev_date = uts_to_date_utc(uts_now(), '%Y%m%d%H%M%SZ')
        rev_reason = 0

        if self.xdb_file:
            # load ca cert and key
            (_ca_key, _ca_cert, ca_id) = self._ca_load()

            serial = cert_serial_get(self.logger, cert)
            if serial:
                serial = '{:X}'.format(serial)

            if ca_id and serial:
                # check if certificate has alreay been revoked:
                if not self._revocation_search('serial', serial):
                    rev_dic = {
                        'caID': ca_id,
                        'serial': serial,
                        'date': rev_date,
                        'invaldate': rev_date,
                        'reasonBit': rev_reason
                    }
                    row_id = self._revocation_insert(rev_dic)
                    if row_id:
                        code = 200
                        message = None
                        detail = None
                    else:
                        code = 500
                        message = 'urn:ietf:params:acme:error:serverInternal'
                        detail = 'database update failed'
                else:
                    code = 400
                    message = 'urn:ietf:params:acme:error:alreadyRevoked'
                    detail = 'Certificate has already been revoked'
            else:
                code = 500
                message = 'urn:ietf:params:acme:error:serverInternal'
                detail = 'certificate lookup failed'
        else:
            code = 500
            message = 'urn:ietf:params:acme:error:serverInternal'
            detail = 'configuration error'

        self.logger.debug('Certificate.revoke() ended')
        return (code, message, detail)
    def revoke(self, content):
        """ revoke request """
        self.logger.debug('Certificate.revoke()')

        response_dic = {}
        # check message
        (code, message, detail, _protected, payload,
         account_name) = self.message.check(content)

        if code == 200:
            if 'certificate' in payload:
                (code, error) = self._revocation_request_validate(
                    account_name, payload)
                if code == 200:
                    # revocation starts here
                    # revocation reason is stored in error variable
                    rev_date = uts_to_date_utc(uts_now())
                    with self.cahandler(self.debug, self.logger) as ca_handler:
                        (code, message,
                         detail) = ca_handler.revoke(payload['certificate'],
                                                     error, rev_date)
                else:
                    message = error
                    detail = None

            else:
                # message could not get decoded
                code = 400
                message = 'urn:ietf:params:acme:error:malformed'
                detail = 'certificate not found'

        # prepare/enrich response
        status_dic = {'code': code, 'message': message, 'detail': detail}
        response_dic = self.message.prepare_response(response_dic, status_dic)

        self.logger.debug(
            'Certificate.revoke() ended with: {0}'.format(response_dic))
        return response_dic
import sys

sys.path.insert(0, '..')
sys.path.insert(1, '.')
import time  # nopep8
from acme_srv.helper import logger_setup, uts_to_date_utc  # nopep8
from acme_srv.housekeeping import Housekeeping  # nopep8

if __name__ == '__main__':

    DEBUG = True

    # initialize logger
    LOGGER = logger_setup(DEBUG)

    SUFFIX = uts_to_date_utc(int(time.time()), '%Y-%m-%d-%H%M%S')

    # this is just for testing
    # from shutil import copyfile
    # copyfile('db.sqlite3.old', 'db.sqlite3')
    # copyfile('acme_srv/acme_srv.db.old', 'acme_srv/acme_srv.db')

    with Housekeeping(DEBUG, LOGGER) as housekeeping:

        # certificate report in json format
        cert_report = housekeeping.certreport_get(
            report_name='certificate_report_{0}'.format(SUFFIX),
            report_format='json')
        # certificate report in csv format
        housekeeping.certreport_get(
            report_name='certificate_report_{0}'.format(SUFFIX))
    def revoke(self, cert, rev_reason='unspecified', rev_date=None):
        """ revoke certificate """
        self.logger.debug('CAhandler.revoke({0}: {1})'.format(rev_reason, rev_date))
        code = None
        message = None
        detail = None

        # overwrite revocation date - we ignore what has been submitted
        rev_date = uts_to_date_utc(uts_now(), '%y%m%d%H%M%SZ')

        if 'issuing_ca_crl' in self.issuer_dict and self.issuer_dict['issuing_ca_crl']:
            # load ca cert and key
            (ca_key, ca_cert) = self._ca_load()
            # turn of chain_check due to issues in pyopenssl (check is not working if key-usage is set)
            # result = self._certificate_chain_verify(cert, ca_cert)

            # proceed if the cert and ca-cert belong together
            # if not result:
            serial = cert_serial_get(self.logger, cert)
            # serial = serial.replace('0x', '')
            if ca_key and ca_cert and serial:
                serial = hex(serial).replace('0x', '')
                if os.path.exists(self.issuer_dict['issuing_ca_crl']):
                    # existing CRL
                    with open(self.issuer_dict['issuing_ca_crl'], 'r') as fso:
                        crl = crypto.load_crl(crypto.FILETYPE_PEM, fso.read())
                    # check CRL already contains serial
                    sn_match = self._crl_check(crl, serial)
                else:
                    # new CRL
                    crl = crypto.CRL()
                    sn_match = None

                # this is the revocation operation
                if not sn_match:
                    revoked = crypto.Revoked()
                    revoked.set_reason(convert_string_to_byte(rev_reason))
                    revoked.set_serial(convert_string_to_byte(serial))
                    revoked.set_rev_date(convert_string_to_byte(rev_date))
                    crl.add_revoked(revoked)
                    # save CRL
                    crl_text = crl.export(ca_cert, ca_key, crypto.FILETYPE_PEM, 7, convert_string_to_byte('sha256'))
                    with open(self.issuer_dict['issuing_ca_crl'], 'wb') as fso:
                        fso.write(crl_text)
                    code = 200
                else:
                    code = 400
                    message = 'urn:ietf:params:acme:error:alreadyRevoked'
                    detail = 'Certificate has already been revoked'
            else:
                code = 400
                message = 'urn:ietf:params:acme:error:serverInternal'
                detail = 'configuration error'
            #else:
            #    code = 400
            #    message = 'urn:ietf:params:acme:error:serverInternal'
            #    detail = result
        else:
            code = 400
            message = 'urn:ietf:params:acme:error:serverInternal'
            detail = 'Unsupported operation'

        self.logger.debug('CAhandler.revoke() ended')
        return(code, message, detail)
    def _authz_info(self, url):
        """ return authzs information """
        self.logger.debug('Authorization._authz_info({0})'.format(url))
        authz_name = url.replace(
            '{0}{1}'.format(self.server_name, self.path_dic['authz_path']), '')
        expires = uts_now() + self.validity
        token = generate_random_string(self.logger, 32)
        authz_info_dic = {}

        # lookup authorization based on name
        try:
            authz = self.dbstore.authorization_lookup('name', authz_name)
        except BaseException as err_:
            self.logger.critical(
                'acme2certifier database error in Authorization._authz_info(): {0}'
                .format(err_))
            authz = None

        if authz:
            # update authorization with expiry date and token (just to be sure)
            try:
                self.dbstore.authorization_update({
                    'name': authz_name,
                    'token': token,
                    'expires': expires
                })
            except BaseException as err_:
                self.logger.error(
                    'acme2certifier database error in Authorization._authz_info(): {0}'
                    .format(err_))
            authz_info_dic['expires'] = uts_to_date_utc(expires)

            # get authorization information from db to be inserted in message
            tnauth = None
            try:
                auth_info = self.dbstore.authorization_lookup(
                    'name', authz_name, ['status__name', 'type', 'value'])
            except BaseException as err_:
                self.logger.error(
                    'acme2certifier database error in Authorization._authz_info(): {0}'
                    .format(err_))
                auth_info = {}

            if auth_info:
                if 'status__name' in auth_info[0]:
                    authz_info_dic['status'] = auth_info[0]['status__name']
                else:
                    authz_info_dic['status'] = 'pending'

                if 'type' in auth_info[0] and 'value' in auth_info[0]:
                    authz_info_dic['identifier'] = {
                        'type': auth_info[0]['type'],
                        'value': auth_info[0]['value']
                    }
                    if auth_info[0]['type'] == 'TNAuthList':
                        tnauth = True
            else:
                authz_info_dic['status'] = 'pending'

            with Challenge(self.debug, self.server_name, self.logger,
                           expires) as challenge:
                # get challenge data (either existing or new ones)
                authz_info_dic['challenges'] = challenge.challengeset_get(
                    authz_name, authz_info_dic['status'], token, tnauth)

        self.logger.debug('Authorization._authz_info() returns: {0}'.format(
            json.dumps(authz_info_dic)))
        return authz_info_dic
    def cleanup(self, timestamp=None, purge=False):
        """ cleanup routine to shrink table-size """
        self.logger.debug('Certificate.cleanup({0},{1})'.format(
            timestamp, purge))

        field_list = [
            'id', 'name', 'expire_uts', 'issue_uts', 'cert', 'cert_raw', 'csr',
            'created_at', 'order__id', 'order__name'
        ]

        # get expired certificates
        try:
            certificate_list = self.dbstore.certificates_search(
                'expire_uts', timestamp, field_list, '<=')
        except BaseException as err_:
            self.logger.critical(
                'acme2certifier database error in Certificate.cleanup() search: {0}'
                .format(err_))
            certificate_list = []

        report_list = []
        for cert in certificate_list:
            (to_be_cleared,
             cert) = self._invalidation_check(cert, timestamp, purge)

            if to_be_cleared:
                report_list.append(cert)

        if not purge:
            # we are just modifiying data
            for cert in report_list:
                data_dic = {
                    'name':
                    cert['name'],
                    'expire_uts':
                    cert['expire_uts'],
                    'issue_uts':
                    cert['issue_uts'],
                    'cert':
                    'removed by certificates.cleanup() on {0} '.format(
                        uts_to_date_utc(timestamp)),
                    'cert_raw':
                    cert['cert_raw']
                }
                try:
                    self.dbstore.certificate_add(data_dic)
                except BaseException as err_:
                    self.logger.critical(
                        'acme2certifier database error in Certificate.cleanup() add: {0}'
                        .format(err_))
        else:
            # delete entries from certificates table
            for cert in report_list:
                try:
                    self.dbstore.certificate_delete('id', cert['id'])
                except BaseException as err_:
                    self.logger.critical(
                        'acme2certifier database error in Certificate.cleanup() delete: {0}'
                        .format(err_))
        self.logger.debug('Certificate.cleanup() ended with: {0} certs'.format(
            len(report_list)))
        return (field_list, report_list)
Exemple #13
0
    def _add(self, payload, aname):
        """ add order request to database """
        self.logger.debug('Order._add({0})'.format(aname))
        error = None
        auth_dic = {}
        order_name = generate_random_string(self.logger, 12)
        expires = uts_now() + self.validity

        if 'identifiers' in payload:

            data_dic = {'status': 2, 'expires': expires, 'account': aname}

            data_dic['name'] = order_name
            data_dic['identifiers'] = json.dumps(payload['identifiers'])

            #if 'notBefore' in payload:
            #    data_dic['notbefore'] = payload['notBefore']
            #if 'notAfter' in payload:
            #    data_dic['notafter'] = payload['notAfter']

            # check identifiers
            error = self._identifiers_check(payload['identifiers'])

            # change order status if needed
            if error:
                data_dic['status'] = 1

            try:
                # add order to db
                oid = self.dbstore.order_add(data_dic)
            except BaseException as err_:
                self.logger.critical(
                    'acme2certifier database error in Order._add() order: {0}'.
                    format(err_))
                oid = None

            if not error:
                if oid:
                    error = None
                    for auth in payload['identifiers']:
                        # generate name
                        auth_name = generate_random_string(self.logger, 12)
                        # store to return to upper func
                        auth_dic[auth_name] = auth.copy()
                        auth['name'] = auth_name
                        auth['order'] = oid
                        auth['status'] = 'pending'
                        auth['expires'] = uts_now() + self.authz_validity
                        try:
                            self.dbstore.authorization_add(auth)
                        except BaseException as err_:
                            self.logger.critical(
                                'acme2certifier database error in Order._add() authz: {0}'
                                .format(err_))
                else:
                    error = 'urn:ietf:params:acme:error:malformed'
        else:
            error = 'urn:ietf:params:acme:error:unsupportedIdentifier'

        self.logger.debug('Order._add() ended')
        return (error, order_name, auth_dic, uts_to_date_utc(expires))