[Server-devel] [PATCH 2/2] xs-activation: Add support for rtcreset messages
Daniel Drake
dsd at laptop.org
Thu Dec 29 15:55:51 EST 2011
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
More information about the Server-devel
mailing list