示例#1
0
    def orders_invalidate(self, uts=uts_now(), report_format='csv', report_name=None):
        """ orders cleanup based on expiry date"""
        self.logger.debug('Housekeeping.orders_invalidate({0})'.format(uts))

        with Order(self.debug, None, self.logger) as order:
            # get expired orders
            (field_list, order_list) = order.invalidate(timestamp=uts)
            # normalize lists
            (field_list, order_list) = self._lists_normalize(field_list, order_list, 'order')
            # convert dates into human readable format
            order_list = self._convert_data(order_list)

            if report_name:
                if order_list:
                    # dump report to file
                    if report_format == 'csv':
                        self.logger.debug('Housekeeping.orders_invalidate(): Dump in csv-format')
                        csv_list = self._to_list(field_list, order_list)
                        self._csv_dump('{0}.{1}'.format(report_name, report_format), csv_list)
                    elif report_format == 'json':
                        self.logger.debug('Housekeeping.orders_invalidate(): Dump in json-format')
                        self._json_dump('{0}.{1}'.format(report_name, report_format), order_list)
                    else:
                        self.logger.debug('Housekeeping.orders_invalidate():  No dump just return report')
                else:
                    self.logger.debug('Housekeeping.orders_invalidate(): No orders to dump')

        return order_list
示例#2
0
    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')
示例#3
0
    def certificates_cleanup(self, uts=None, purge=False, report_format='csv', report_name=None):
        """ database cleanuip certificate-table """
        self.logger.debug('Housekeeping.certificates_cleanup()')
        if not uts:
            uts = uts_now()

        with Certificate(self.debug, None, self.logger) as certificate:
            (field_list, cert_list) = certificate.cleanup(timestamp=uts, purge=purge)

            # normalize lists
            # (field_list, cert_list) = self._lists_normalize(field_list, cert_list, 'certificate')

            if report_name:
                if cert_list:
                    # dump report to file
                    if report_format == 'csv':
                        self.logger.debug('Housekeeping.certificates_cleanup(): Dump in csv-format')
                        csv_list = self._to_list(field_list, cert_list)
                        self._csv_dump('{0}.{1}'.format(report_name, report_format), csv_list)
                    elif report_format == 'json':
                        self.logger.debug('Housekeeping.certificates_cleanup(): Dump in json-format')
                        self._json_dump('{0}.{1}'.format(report_name, report_format), cert_list)
                    else:
                        self.logger.debug('Housekeeping.certificates_cleanup():  No dump just return report')
                else:
                    self.logger.debug('Housekeeping.certificates_cleanup(): No certificates to dump')

        return cert_list
示例#4
0
    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
示例#5
0
    def _validate(self, challenge_name, payload):
        """ validate challenge"""
        self.logger.debug('Challenge._validate({0}: {1})'.format(challenge_name, payload))
        if self.challenge_validation_disable:
            self.logger.debug('CHALLENGE VALIDATION DISABLED. SETTING challenge status to valid')
            challenge_check = True
            invalid = False
        else:
            (challenge_check, invalid) = self._check(challenge_name, payload)

        if invalid:
            self._update({'name' : challenge_name, 'status' : 'invalid'})
            # authorization update to valid state
            self._update_authz(challenge_name, {'status' : 'invalid'})
        elif challenge_check:
            self._update({'name' : challenge_name, 'status' : 'valid', 'validated': uts_now()})
            # authorization update to valid state
            self._update_authz(challenge_name, {'status' : 'valid'})

        if payload:
            if 'keyAuthorization' in payload:
                # update challenge to ready state
                data_dic = {'name' : challenge_name, 'keyauthorization' : payload['keyAuthorization']}
                self._update(data_dic)

        self.logger.debug('Challenge._validate() ended with:{0}'.format(challenge_check))
        return challenge_check
    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)
示例#7
0
    def _csr_id_lookup(self, csr_cn, csr_san_list, csr=None):
        """ lookup CSR based on CN """
        self.logger.debug('CAhandler._csr_id_lookup()')

        uts_n = uts_now()

        # get unused requests from NCLM
        unused_request_list = self._unusedrequests_get()

        # get last 50 requests
        if not csr_cn:
            last_request_list = self._lastrequests_get()
        else:
            last_request_list = []

        req_id = None
        try:
            # check every CSR
            for req in sorted(unused_request_list,
                              key=lambda i: i['requestID'],
                              reverse=True):
                req_cn = None
                # check the import date and consider only csr which are less then 5min old
                csr_uts = date_to_uts_utc(req['addedAt'][:25],
                                          '%Y-%m-%dT%H:%M:%S.%f')
                if uts_n - csr_uts < self.request_delta_treshold:
                    if 'subjectName' in req:
                        # split the subject and filter CN
                        subject_list = req['subjectName'].split(',')
                        for field in subject_list:
                            field = field.strip()
                            if field.startswith('CN='):
                                req_cn = field.lower().replace('cn=', '')
                                break

                    if csr_cn:
                        if req_cn == csr_cn.lower() and 'requestID' in req:
                            req_id = req['requestID']
                            break
                    else:
                        for _req in sorted(last_request_list,
                                           key=lambda i: i['requestId'],
                                           reverse=True):
                            if 'pkcs10' in _req:
                                if _req['pkcs10'] == csr:
                                    req_id = _req['requestId']
                                    break
        except Exception as err_:
            self.logger.error(
                '_csr_id_lookup(): response incomplete: {0}'.format(err_))

        self.logger.debug(
            'CAhandler._csr_id_lookup() ended with: {0}'.format(req_id))
        return req_id
    def _csr_id_lookup(self, csr_cn, csr_san_list):
        """ lookup CSR based on CN """
        self.logger.debug('CAhandler._csr_id_lookup()')

        uts_n = uts_now()

        # get unused requests from NCLM
        request_list = self._unusedrequests_get()
        req_id = None
        # check every CSR
        for req in request_list:
            req_cn = None
            # check the import date and consider only csr which are less then 5min old
            csr_uts = date_to_uts_utc(req['addedAt'][:25],
                                      '%Y-%m-%dT%H:%M:%S.%f')
            if uts_n - csr_uts < 300:
                if 'subjectName' in req:
                    # split the subject and filter CN
                    subject_list = req['subjectName'].split(',')
                    for field in subject_list:
                        field = field.strip()
                        if field.startswith('CN='):
                            req_cn = field.lower().replace('cn=', '')
                            break
                # compare csr cn with request cn
                if csr_cn:
                    if req_cn == csr_cn.lower() and 'requestID' in req:
                        req_id = req['requestID']
                        break
                elif not req_cn:
                    # special certbot scenario (no CN in CSR). No better idea how to handle this, take first request
                    try:
                        req_all = requests.get(self.api_host + '/requests',
                                               headers=self.headers,
                                               verify=self.ca_bundle,
                                               proxies=self.proxy).json()
                    except BaseException as err_:
                        self.logger.error(
                            'CAhandler._csr_id_lookup() returned error: {0}'.
                            format(str(err_)))
                        req_all = []

                    for _req in reversed(req_all):
                        if 'pkcs10' in _req:
                            req_san_list = csr_san_get(self.logger,
                                                       _req['pkcs10'])
                            if sorted(csr_san_list) == sorted(
                                    req_san_list) and 'requestID' in _req:
                                req_id = _req['requestID']
                                break
        self.logger.debug(
            'CAhandler._csr_id_lookup() ended with: {0}'.format(req_id))
        return req_id
示例#9
0
    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)
示例#10
0
    def _cert_reusage_check(self, csr):
        """ check if an existing certificate an be reused """
        self.logger.debug('Certificate._cert_reusage_check({0})'.format(
            self.cert_reusage_timeframe))

        try:
            result_dic = self.dbstore.certificates_search(
                'csr', csr, ('cert', 'cert_raw', 'expire_uts', 'issue_uts',
                             'created_at', 'id'))
        except Exception as err_:
            self.logger.critical(
                'acme2certifier database error in Certificate._cert_reusage_check(): {0}'
                .format(err_))
            result_dic = None

        cert = None
        cert_raw = None
        message = None

        if result_dic:
            uts = uts_now()
            # sort certificates by creation date
            for certificate in sorted(result_dic,
                                      key=lambda i: i['issue_uts'],
                                      reverse=True):
                try:
                    uts_create = date_to_uts_utc(certificate['created_at'])
                except Exception as _err:
                    self.logger.error(
                        'acme2certifier date_to_uts_utc() error in Certificate._cert_reusage_check(): id:{0}/created_at:{1}'
                        .format(certificate['id'], certificate['created_at']))
                    uts_create = 0

                # check if there certificates within reusage timeframe
                if certificate['cert_raw'] and certificate[
                        'cert'] and uts - self.cert_reusage_timeframe <= uts_create:
                    # exclude expired certificates
                    if uts <= certificate['expire_uts']:
                        cert = certificate['cert']
                        cert_raw = certificate['cert_raw']
                        message = 'reused certificate from id: {0}'.format(
                            certificate['id'])
                        break

        self.logger.debug(
            'Certificate._cert_reusage_check() ended with {0}'.format(message))
        return (None, cert, cert_raw, message)
    def invalidate(self, timestamp=None):
        """ invalidate authorizations """
        self.logger.debug('Authorization.invalidate({0})'.format(timestamp))
        if not timestamp:
            timestamp = uts_now()
            self.logger.debug(
                'Authorization.invalidate(): set timestamp to {0}'.format(
                    timestamp))

        field_list = [
            'id', 'name', 'expires', 'value', 'created_at', 'token',
            'status__id', 'status__name', 'order__id', 'order__name'
        ]
        try:
            authz_list = self.dbstore.authorizations_expired_search(
                'expires', timestamp, vlist=field_list, operant='<=')
        except BaseException as err_:
            self.logger.critical(
                'acme2certifier database error in Authorization.invalidate(): {0}'
                .format(err_))
            authz_list = []

        output_list = []
        for authz in authz_list:
            # select all authz which are not invalid
            if 'name' in authz and 'status__name' in authz and authz[
                    'status__name'] != 'expired':
                # skip corner cases where authz expiry is set to 0
                if 'expires' not in authz or authz['expires'] > 0:
                    # change status and add to output list
                    output_list.append(authz)
                    data_dic = {'name': authz['name'], 'status': 'expired'}
                    try:
                        self.dbstore.authorization_update(data_dic)
                    except BaseException as err_:
                        self.logger.critical(
                            'acme2certifier database error in Authorization.invalidate(): {0}'
                            .format(err_))

        self.logger.debug(
            'Authorization.invalidate() ended: {0} authorizations identified'.
            format(len(output_list)))
        return (field_list, output_list)
示例#12
0
    def invalidate(self, timestamp=None):
        """ invalidate orders """
        self.logger.debug('Order.invalidate({0})'.format(timestamp))
        if not timestamp:
            timestamp = uts_now()
            self.logger.debug(
                'Order.invalidate(): set timestamp to {0}'.format(timestamp))

        field_list = [
            'id', 'name', 'expires', 'identifiers', 'created_at', 'status__id',
            'status__name', 'account__id', 'account__name', 'account__contact'
        ]
        try:
            order_list = self.dbstore.orders_invalid_search('expires',
                                                            timestamp,
                                                            vlist=field_list,
                                                            operant='<=')
        except BaseException as err_:
            self.logger.critical(
                'acme2certifier database error in Order._invalidate() search: {0}'
                .format(err_))
            order_list = []
        output_list = []
        for order in order_list:
            # print(order['id'])
            # select all orders which are not invalid
            if 'name' in order and 'status__name' in order and order[
                    'status__name'] != 'invalid':
                # change status and add to output list
                output_list.append(order)
                data_dic = {'name': order['name'], 'status': 'invalid'}
                try:
                    self.dbstore.order_update(data_dic)
                except BaseException as err_:
                    self.logger.critical(
                        'acme2certifier database error in Order._invalidate() upd: {0}'
                        .format(err_))

        self.logger.debug(
            'Order.invalidate() ended: {0} orders identified'.format(
                len(output_list)))
        return (field_list, output_list)
示例#13
0
    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
    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
示例#16
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))