Upon receiving a rtcreset request in xs-activation-tcpserver, the oat class and the activation signer will generate a RTC timestamp reset signature and send it to the client. When the client reboots, the server's current date/time (recorded in rtcreset.sig) will be programmed into the rtc-timestamp field.
This allows for automated recovery of corrupt or invalid rtc-timestamp fields: http://wiki.laptop.org/go/RTC_Anti-rollback It depends on a new olpc-bios-crypto RPM including the make-reset command. Common code in oat and xs-activation-signer was factored out to prevent further duplication. --- oat.py | 91 ++++++++++++++++++++++++++------------------ xs-activation-signer.py | 81 +++++++++++++++++++++++++++++++++------ xs-activation-tcpserver.py | 28 ++++++++++++- 3 files changed, 148 insertions(+), 52 deletions(-) diff --git a/oat.py b/oat.py index 4c5cbaa..bb22a23 100644 --- a/oat.py +++ b/oat.py @@ -77,6 +77,33 @@ class oat: mdbc.execute(sql, {'timenow': int(time.time()), 'sn': sn}) mdbh.commit() + def get_signed_output(self, fname): + """ + Watch for output file from signing helper and return its contents, or + None if a timeout occurred. The output file is automatically deleted. + """ + + destpath = '/var/lib/xs-activation/done/' + fname + output = None + + # wait for up to X seconds + timestart = time.time() + while (time.time() - timestart) < 3: + if not os.path.exists(destpath): + time.sleep(0.20) + continue + try: + fh = open(destpath) + output = fh.read() + fh.close() + os.unlink(destpath) + break + except: + self.log_error("Error handling response from signer script") + break + + return output + def get_rescue_leases(self): """Note: will write (potentially lots) of cjson formatted stuff to to STDOUT""" # get SNs of stolen machines @@ -118,25 +145,12 @@ class oat: os.close(fh) os.rename(tmpfpath, '/var/lib/xs-activation/req/' + fname) - destpath = '/var/lib/xs-activation/done/' + fname - # wait for up to X seconds - timestart = time.time() - while (time.time() - timestart) < 3: - if not os.path.exists(destpath): - time.sleep(0.20) - continue - try: - fh = open(destpath) - for line in fh: - sys.stdout.write(line) - fh.close() - os.unlink(destpath) - break - except: - self.log_error("Error handling response from signer script") - return False - - return True + output = self.get_signed_output(fname) + if output: + sys.stdout.write(output) + return True + else: + return False def in_list(self, haystack, needle): @@ -241,7 +255,13 @@ class oat: return response - + def get_rtcreset(self, sn, currentrtc, nonce): + # attempt to build a sig02 delegated rtcreset + kpath = self.get_key_path() + ldpath = self.get_lease_delegation_path(sn) + if kpath and ldpath: + newrtc = datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") + return self.generate_delegated_rtcreset(sn, currentrtc, nonce, newrtc) def mdb_available(self): """Check if it's available - so that we can serve @@ -297,29 +317,26 @@ class oat: cmd = ['/bin/touch', reqpath ] subprocess.check_call(cmd) - destpath = '/var/lib/xs-activation/done/' + fname - # wait for up to X seconds - timestart = time.time() - lease = None - while (time.time() - timestart) < 3: - if not os.path.exists(destpath): - time.sleep(0.20) - continue - try: - fh = open(destpath) - lease = fh.read() - fh.close() - os.unlink(destpath) - break - except: - self.log_error("Error handling response from signer script") - break + lease = self.get_signed_output(fname) if lease == None: self.log_error("Timed out waiting for signed response") raise RuntimeError("Timed out waiting for signed response") return lease; + def generate_delegated_rtcreset(self, sn, currentrtc, nonce, newrtc): + fname = "rtc01delegated_%s_%s_%s_%s_%s" % (sn, currentrtc, nonce, newrtc, hexlify(os.urandom(8))) + reqpath = '/var/lib/xs-activation/req/' + fname + cmd = ['/bin/touch', reqpath ] + subprocess.check_call(cmd) + + rtcreset = self.get_signed_output(fname) + if rtcreset == None: + self.log_error("Timed out waiting for signed response") + raise RuntimeError("Timed out waiting for signed response") + + return rtcreset + def sign_sig02(self, sn, absexpiry, str): """Make a delegated OAT signature on an arbitrary bit of content""" diff --git a/xs-activation-signer.py b/xs-activation-signer.py index 9700b8e..46ccec2 100755 --- a/xs-activation-signer.py +++ b/xs-activation-signer.py @@ -148,23 +148,15 @@ def serve_delegated_lease(dirpath, fname, fpath, params): destpath = '/var/lib/xs-activation/done/' + fname save_atomically(destpath, lease) -def generate_delegated_lease(sn, expiry): - - myoat = oat.oat() - - ldpath = myoat.get_lease_delegation_path(sn) - if not ldpath: - log_error("No lease deleation for " + sn) - exit(1) - - ld = open(ldpath) +def parse_lease_delegation(path, sn): + ld = open(path) ldstr = ld.read() ld.close() # check if not ('del01:' == ldstr[0:6] and sn == ldstr[7:18]): - log_error("Strange delegation header in " + ldpath) + log_error("Strange delegation header in " + path) exit(1) ldstr = ldstr[19:] @@ -177,7 +169,21 @@ def generate_delegated_lease(sn, expiry): # delegation file that the script expects ldstr = ldstr[len(uuid)+1:] - # write it to a tmpfile + return uuid, ldstr + +def generate_delegated_lease(sn, expiry): + + myoat = oat.oat() + + ldpath = myoat.get_lease_delegation_path(sn) + if not ldpath: + log_error("No lease deleation for " + sn) + exit(1) + + # extract UUID and delegation + uuid, ldstr = parse_lease_delegation(ldpath, sn) + + # write delegation to a tmpfile (dfh, dfpath) = tempfile.mkstemp() os.write(dfh, ldstr) os.close(dfh) @@ -219,6 +225,55 @@ def generate_multiple_delegated_leases(dirpath, fname, fpath, params): destpath = '/var/lib/xs-activation/done/' + fname save_atomically(destpath, cjson.write([1,leases])) +def serve_delegated_rtcreset(dirpath, fname, fpath, params): + # remove empty/unused file + os.unlink(fpath) + + randid = params.pop() + newrtc = params.pop(); + nonce = params.pop(); + currentrtc = params.pop(); + + sn = params.pop(); + if not validate_sn(sn): + raise RuntimeError('Invalid SN') + + if len(currentrtc) != 16 or currentrtc[15] != 'Z' or currentrtc[8] != 'T': + log_error("Unrecognised rtcreset timestamp") + exit(1) + + if not nonce.isdigit(): + log_error("Unrecognised rtcreset nonce") + exit(1) + + # find uuid and delegation + myoat = oat.oat() + + ldpath = myoat.get_lease_delegation_path(sn) + if not ldpath: + log_error("No lease delegation for " + sn) + exit(1) + + # extract UUID and delegation + uuid, ldstr = parse_lease_delegation(ldpath, sn) + + # write delegation to a tmpfile + (dfh, dfpath) = tempfile.mkstemp() + os.write(dfh, ldstr) + os.close(dfh) + + # prep params + cmd = ['/usr/bin/obc-make-rtcreset', + '--chain', dfpath, + '--signingkey', kpath, + sn, uuid, currentrtc, nonce, newrtc] + rtcreset = subprocess.Popen(cmd, + stdout=subprocess.PIPE).communicate()[0] + os.unlink(dfpath) + + destpath = '/var/lib/xs-activation/done/' + fname + save_atomically(destpath, rtcreset) + def cleanup(fpath): try: os.unlink(fpath) @@ -268,6 +323,8 @@ try: serve_delegated_lease(dirpath, fname, fpath, params) elif cmd == 'multiact01': generate_multiple_delegated_leases(dirpath, fname, fpath, params) + elif cmd == 'rtc01delegated': + serve_delegated_rtcreset(dirpath, fname, fpath, params) else: log_error('unknown command ' + cmd) exit(1) diff --git a/xs-activation-tcpserver.py b/xs-activation-tcpserver.py index 942eaf1..5556cb5 100755 --- a/xs-activation-tcpserver.py +++ b/xs-activation-tcpserver.py @@ -31,9 +31,10 @@ def log_error(str): # handle input - we are expecting # - a 11 char-long serial number -# - the string of no more than 10 characters, followed by -# a ':' and a space, followed by additional data -# for example - 'time01: serialnum' +# - the string "time01: " followed by a 11-character serial number, a space, +# and a 22 character nonce +# - the string "rtcreset " followed by 3 space separated parameters: serial +# number, current rtc-timestamp value, current rtc-count nonce # there's no newline expected def check_stolen(sn): @@ -92,6 +93,25 @@ def send_time(req): else: sys.stderr.write("Could not serve time request for %s - probable cause: no lease available\n" % sn) +def send_rtcreset(req): + # remove rtcreset prefix + req = req[9:] + params = req.strip().split(' ') + if len(params) != 3: + log_error("Insufficient pararameters for rtcreset request") + exit(1) + + sn = params[0] + currentrtc = params[1] + nonce = params[2] + + msg = myoat.get_rtcreset(sn, currentrtc, nonce) + if msg: + sys.stdout.write(msg) + exit() + else: + sys.stderr.write("Could not serve rtcreset request for %s - probable cause: no delegation available\n" % sn) + ######## Main ########### # wait for data to arrive @@ -104,6 +124,8 @@ if re.match('^[A-Z0-9]{11}$', req): send_lease(req) elif req.startswith('time01: '): send_time(req) +elif req.startswith('rtcreset '): + send_rtcreset(req) else: # unknown command? log up to 48 chars to minimize chance of DoS flood log_error("unknown request: " + req[:48]) -- 1.7.7.4 _______________________________________________ Server-devel mailing list [email protected] http://lists.laptop.org/listinfo/server-devel
