From f7e7d0572905fe428a7d03a1ca2a41b3a834dad8 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Fri, 6 Nov 2020 10:00:36 -0500 Subject: [PATCH] Add TPM2 support to node api key handling This is an optional capability that image payloads may use to use the TPM2 to protect an apikey as an alternative to arming a weak authentication invocation --- confluent_osdeploy/utils/clortho.c | 19 +++++++++++++--- .../confluent/config/attributes.py | 8 +++++++ confluent_server/confluent/credserver.py | 22 +++++++++++++++++-- confluent_server/confluent/selfservice.py | 2 ++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/confluent_osdeploy/utils/clortho.c b/confluent_osdeploy/utils/clortho.c index 94918189..97d63472 100644 --- a/confluent_osdeploy/utils/clortho.c +++ b/confluent_osdeploy/utils/clortho.c @@ -36,7 +36,8 @@ unsigned char* genpasswd(int len) { int main(int argc, char* argv[]) { int sock, ret; char slen; - unsigned char currlen, currtype; + unsigned char currtype; + size_t currlen; unsigned char* passwd; unsigned char* cryptedpass; unsigned char* macaddr; @@ -107,10 +108,18 @@ int main(int argc, char* argv[]) { ret = read(sock, buffer, 2); while (buffer[0] != 255) { currtype = buffer[0]; - currlen = buffer[1]; + if (currtype & 0b10000000) { + currlen = buffer[1] << 8 & buffer[2]; + } else { + currlen = buffer[1]; + } memset(buffer, 0, MAXPACKET); + if (currlen > 1000) { + fprintf(stderr, "Received oversized message\n"); + exit(1); + } if (currlen) { - ret = read(sock, buffer, currlen); // Max is 255, well under MAX_PACKET + ret = read(sock, buffer, currlen); // Max is 1000, well under MAX_PACKET } if (currtype == 2) { dprintf(sock, "\x03%c", currlen); @@ -118,6 +127,10 @@ int main(int argc, char* argv[]) { slen = strlen(cryptedpass) & 0xff; dprintf(sock, "\x04%c%s", slen, cryptedpass); ret = write(sock, "\x00\x00", 2); + } else if (currtype == 128) { + printf("SEALED:%s", buffer); + printf("\n"); + exit(0); } else if (currtype == 5) { printf("%s", passwd); printf("\n"); diff --git a/confluent_server/confluent/config/attributes.py b/confluent_server/confluent/config/attributes.py index 5f10e1be..7c5506aa 100644 --- a/confluent_server/confluent/config/attributes.py +++ b/confluent_server/confluent/config/attributes.py @@ -129,6 +129,14 @@ node = { 'Generally this is not directly modified, but is modified ' 'by the "nodedeploy" command'), }, + 'deployment.sealedapikey': { + 'description': 'This attribute is used by some images to save a sealed ' + 'version of a node apikey, so that a subsequent run with ' + 'same TPM2 will use the TPM2 to protect the API key rather ' + 'than local network verification. If this is set, then ' + 'an api key request will receive this if the api key grant ' + 'is not armed', + }, #'id': { # 'description': ('Numeric identifier for node') #}, diff --git a/confluent_server/confluent/credserver.py b/confluent_server/confluent/credserver.py index f6632d00..7e2040ce 100644 --- a/confluent_server/confluent/credserver.py +++ b/confluent_server/confluent/credserver.py @@ -23,6 +23,15 @@ import eventlet.green.socket as socket import eventlet.greenpool import os +# cred grant tlvs: +# 0, 0 - null +# 1, len, +# 2, len, token - echo request +# 3, len, token - echo reply +# 4, len, crypted - crypted apikey +# 5, 0, accept key +# 128, len, len, key - sealed key + class CredServer(object): def __init__(self): self.cfm = cfm.ConfigManager(None) @@ -38,11 +47,20 @@ class CredServer(object): client.close() return nodename = util.stringify(client.recv(tlv[1])) - tlv = bytearray(client.recv(2)) - apiarmed = self.cfm.get_node_attributes(nodename, 'deployment.apiarmed') + tlv = bytearray(client.recv(2)) # should always be null + apiarmed = self.cfm.get_node_attributes(nodename, + ['deployment.apiarmed', 'deployment.sealedapikey']) apiarmed = apiarmed.get(nodename, {}).get('deployment.apiarmed', {}).get( 'value', None) if not apiarmed: + if apiarmed.get(nodename, {}).get( + 'deployment.sealedapikey', {}).get('value', None): + sealed = apiarmed[nodename]['deployment.sealedapikey'][ + 'value'] + if not isintance(sealed, bytes): + sealed = sealed.encode('utf8') + reply = b'\x80' + struct.pack('>H', len(sealed) + 1) + sealed + b'\x00' + client.send(reply) client.close() return if apiarmed not in ('once', 'continuous'): diff --git a/confluent_server/confluent/selfservice.py b/confluent_server/confluent/selfservice.py index 7e286a41..b8f45265 100644 --- a/confluent_server/confluent/selfservice.py +++ b/confluent_server/confluent/selfservice.py @@ -256,6 +256,8 @@ def handle_request(env, start_response): else: start_response('500 Error', (('Content-Type', 'text/plain'),)) yield 'No pending profile detected, unable to accept status update' + elif env['PATH_INFO'] == '/self/savetoken': + print(repr(reqbody)) else: start_response('404 Not Found', ()) yield 'Not found'