15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""A bare-bones test server for testing cloud policy support. 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)This implements a simple cloud policy test server that can be used to test 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)chrome's device management service client. The policy information is read from 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)the file named device_management in the server's data directory. It contains 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enforced and recommended policies for the device and user scope, and a list 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)of managed users. 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The format of the file is JSON. The root dictionary contains a list under the 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)key "managed_users". It contains auth tokens for which the server will claim 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)that the user is managed. The token string "*" indicates that all users are 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)claimed to be managed. Other keys in the root dictionary identify request 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)scopes. The user-request scope is described by a dictionary that holds two 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub-dictionaries: "mandatory" and "recommended". Both these hold the policy 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)definitions as key/value stores, their format is identical to what the Linux 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)implementation reads from /etc. 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)The device-scope holds the policy-definition directly as key/value stores in the 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)protobuf-format. 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Example: 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){ 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "google/chromeos/device" : { 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "guest_mode_enabled" : false 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }, 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "google/chromeos/user" : { 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "mandatory" : { 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "HomepageLocation" : "http://www.chromium.org", 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "IncognitoEnabled" : false 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }, 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "recommended" : { 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "JavascriptEnabled": false 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }, 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "google/chromeos/publicaccount/user@example.com" : { 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "mandatory" : { 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "HomepageLocation" : "http://www.chromium.org" 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }, 432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "recommended" : { 442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) }, 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "managed_users" : [ 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "secret123456" 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ], 49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) "current_key_index": 0, 50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) "robot_api_auth_code": "fake_auth_code" 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import BaseHTTPServer 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import cgi 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import google.protobuf.text_format 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import hashlib 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import random 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import tlslite 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import tlslite.api 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import tlslite.utils 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import tlslite.utils.cryptomath 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The name and availability of the json module varies in python versions. 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)try: 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import simplejson as json 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)except ImportError: 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) import json 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except ImportError: 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) json = None 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import asn1der 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import testserver_base 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import device_management_backend_pb2 as dm 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import cloud_policy_pb2 as cp 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import chrome_extension_policy_pb2 as ep 852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)# Device policy is only available on Chrome OS builds. 872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)try: 882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) import chrome_device_policy_pb2 as dp 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)except ImportError: 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) dp = None 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# ASN.1 object identifier for PKCS#1/RSA. 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PKCS1_RSA_OID = '\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01' 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# SHA256 sum of "0". 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SHA256_0 = hashlib.sha256('0').digest() 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# List of bad machine identifiers that trigger the |valid_serial_number_missing| 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# flag to be set set in the policy fetch response. 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)BAD_MACHINE_IDS = [ '123490EN400015' ] 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# List of machines that trigger the server to send kiosk enrollment response 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# for the register request. 1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)KIOSK_MACHINE_IDS = [ 'KIOSK' ] 1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class PolicyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Decodes and handles device management requests from clients. 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The handler implements all the request parsing and protobuf message decoding 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) and encoding. It calls back into the server to lookup, register, and 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unregister clients. 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def __init__(self, request, client_address, server): 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Initialize the handler. 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) request: The request data received from the client as a string. 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) client_address: The client address. 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) server: The TestServer object to use for (un)registering clients. 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request, 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) client_address, server) 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetUniqueParam(self, name): 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Extracts a unique query parameter from the request. 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name: Names the parameter to fetch. 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The parameter value or None if the parameter doesn't exist or is not 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unique. 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if not hasattr(self, '_params'): 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self._params = cgi.parse_qs(self.path[self.path.find('?') + 1:]) 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) param_list = self._params.get(name, []) 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(param_list) == 1: 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return param_list[0] 1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return None 1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def do_GET(self): 1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Handles GET requests. 1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Currently this is only used to serve external policy data.""" 1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sep = self.path.find('?') 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) path = self.path if sep == -1 else self.path[:sep] 1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if path == '/externalpolicydata': 1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) http_response, raw_reply = self.HandleExternalPolicyDataRequest() 1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) else: 1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) http_response = 404 1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) raw_reply = 'Invalid path' 1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.send_response(http_response) 1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.end_headers() 1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.wfile.write(raw_reply) 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def do_POST(self): 1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) http_response, raw_reply = self.HandleRequest() 1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.send_response(http_response) 1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (http_response == 200): 1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.send_header('Content-Type', 'application/x-protobuffer') 1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.end_headers() 1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.wfile.write(raw_reply) 1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def HandleExternalPolicyDataRequest(self): 1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Handles a request to download policy data for a component.""" 1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_key = self.GetUniqueParam('key') 1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if not policy_key: 1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return (400, 'Missing key parameter') 1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) data = self.server.ReadPolicyDataFromDataDir(policy_key) 1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if data is None: 1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return (404, 'Policy not found for ' + policy_key) 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return (200, data) 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def HandleRequest(self): 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Handles a request. 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Parses the data supplied at construction time and returns a pair indicating 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) http status code and response data to be sent back to the client. 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A tuple of HTTP status code and response data to send to the client. 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rmsg = dm.DeviceManagementRequest() 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) length = int(self.headers.getheader('content-length')) 1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) rmsg.ParseFromString(self.rfile.read(length)) 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('gaia auth token -> ' + 1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.headers.getheader('Authorization', '')) 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('oauth token -> ' + str(self.GetUniqueParam('oauth_token'))) 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('deviceid -> ' + str(self.GetUniqueParam('deviceid'))) 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.DumpMessage('Request', rmsg) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) request_type = self.GetUniqueParam('request') 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check server side requirements, as defined in 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # device_management_backend.proto. 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (self.GetUniqueParam('devicetype') != '2' or 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.GetUniqueParam('apptype') != 'Chrome' or 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (request_type != 'ping' and 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) len(self.GetUniqueParam('deviceid')) >= 64) or 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) len(self.GetUniqueParam('agent')) >= 64): 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (400, 'Invalid request parameter') 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if request_type == 'register': 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.ProcessRegister(rmsg.register_request) 206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if request_type == 'api_authorization': 207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return self.ProcessApiAuthorization(rmsg.service_api_access_request) 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif request_type == 'unregister': 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.ProcessUnregister(rmsg.unregister_request) 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif request_type == 'policy' or request_type == 'ping': 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.ProcessPolicy(rmsg.policy_request, request_type) 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif request_type == 'enterprise_check': 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self.ProcessAutoEnrollment(rmsg.auto_enrollment_request) 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (400, 'Invalid request parameter') 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def CreatePolicyForExternalPolicyData(self, policy_key): 2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Returns an ExternalPolicyData protobuf for policy_key. 2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) If there is policy data for policy_key then the download url will be 2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) set so that it points to that data, and the appropriate hash is also set. 2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Otherwise, the protobuf will be empty. 2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Args: 2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_key: the policy type and settings entity id, joined by '/'. 2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Returns: 2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) A serialized ExternalPolicyData. 2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """ 2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) settings = ep.ExternalPolicyData() 2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) data = self.server.ReadPolicyDataFromDataDir(policy_key) 2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if data: 2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) settings.download_url = ('http://%s:%s/externalpolicydata?key=%s' % 2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) (self.server.server_name, 2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.server.server_port, 2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_key) ) 2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) settings.secure_hash = hashlib.sha1(data).digest() 2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return settings.SerializeToString() 2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def CheckGoogleLogin(self): 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Extracts the auth token from the request and returns it. The token may 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) either be a GoogleLogin token from an Authorization header, or an OAuth V2 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token from the oauth_token query parameter. Returns None if no token is 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) present. 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) oauth_token = self.GetUniqueParam('oauth_token') 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if oauth_token: 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return oauth_token 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) match = re.match('GoogleLogin auth=(\\w+)', 2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.headers.getheader('Authorization', '')) 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if match: 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return match.group(1) 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return None 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def ProcessRegister(self, msg): 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Handles a register request. 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Checks the query for authorization and device identifier, registers the 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) device with the server and constructs a response. 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg: The DeviceRegisterRequest message received from the client. 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A tuple of HTTP status code and response data to send to the client. 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check the auth token and device ID. 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auth = self.CheckGoogleLogin() 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not auth: 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (403, 'No authorization') 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy = self.server.GetPolicies() 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if ('*' not in policy['managed_users'] and 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auth not in policy['managed_users']): 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (403, 'Unmanaged') 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) device_id = self.GetUniqueParam('deviceid') 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not device_id: 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (400, 'Missing device identifier') 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) token_info = self.server.RegisterDevice(device_id, 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg.machine_id, 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg.type) 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Send back the reply. 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = dm.DeviceManagementResponse() 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response.register_response.device_management_token = ( 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token_info['device_token']) 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response.register_response.machine_name = token_info['machine_name'] 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response.register_response.enrollment_type = token_info['enrollment_mode'] 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.DumpMessage('Response', response) 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (200, response.SerializeToString()) 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 298c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) def ProcessApiAuthorization(self, msg): 299c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) """Handles an API authorization request. 300c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 301c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) Args: 302c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) msg: The DeviceServiceApiAccessRequest message received from the client. 303c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) Returns: 305c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) A tuple of HTTP status code and response data to send to the client. 306c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) """ 307c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) policy = self.server.GetPolicies() 308c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 309c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # Return the auth code from the config file if it's defined, 310c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) # else return a descriptive default value. 311c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) response = dm.DeviceManagementResponse() 312c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) response.service_api_access_response.auth_code = policy.get( 313bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 'robot_api_auth_code', 'policy_testserver.py-auth_code') 314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) self.DumpMessage('Response', response) 315c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return (200, response.SerializeToString()) 317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def ProcessUnregister(self, msg): 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Handles a register request. 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Checks for authorization, unregisters the device and constructs the 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response. 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg: The DeviceUnregisterRequest message received from the client. 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A tuple of HTTP status code and response data to send to the client. 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check the management token. 3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) token, response = self.CheckToken() 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not token: 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return response 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Unregister the device. 3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.server.UnregisterDevice(token['device_token']) 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Prepare and send the response. 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = dm.DeviceManagementResponse() 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response.unregister_response.CopyFrom(dm.DeviceUnregisterResponse()) 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.DumpMessage('Response', response) 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (200, response.SerializeToString()) 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def ProcessPolicy(self, msg, request_type): 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Handles a policy request. 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Checks for authorization, encodes the policy into protobuf representation 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) and constructs the response. 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg: The DevicePolicyRequest message received from the client. 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A tuple of HTTP status code and response data to send to the client. 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) token_info, error = self.CheckToken() 3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if not token_info: 3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return error 3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response = dm.DeviceManagementResponse() 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for request in msg.request: 3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) fetch_response = response.policy_response.response.add() 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (request.policy_type in 3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ('google/chrome/user', 3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'google/chromeos/user', 3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'google/chromeos/device', 3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'google/chromeos/publicaccount', 3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'google/chrome/extension')): 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if request_type != 'policy': 3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) fetch_response.error_code = 400 3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) fetch_response.error_message = 'Invalid request type' 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.ProcessCloudPolicy(request, token_info, fetch_response) 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) fetch_response.error_code = 400 3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) fetch_response.error_message = 'Invalid policy_type' 3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return (200, response.SerializeToString()) 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def ProcessAutoEnrollment(self, msg): 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Handles an auto-enrollment check request. 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The reply depends on the value of the modulus: 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1: replies with no new modulus and the sha256 hash of "0" 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2: replies with a new modulus, 4. 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4: replies with a new modulus, 2. 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8: fails with error 400. 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 16: replies with a new modulus, 16. 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 32: replies with a new modulus, 1. 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) anything else: replies with no new modulus and an empty list of hashes 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) These allow the client to pick the testing scenario its wants to simulate. 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg: The DeviceAutoEnrollmentRequest message received from the client. 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A tuple of HTTP status code and response data to send to the client. 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auto_enrollment_response = dm.DeviceAutoEnrollmentResponse() 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if msg.modulus == 1: 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auto_enrollment_response.hash.append(SHA256_0) 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif msg.modulus == 2: 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auto_enrollment_response.expected_modulus = 4 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif msg.modulus == 4: 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auto_enrollment_response.expected_modulus = 2 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif msg.modulus == 8: 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (400, 'Server error') 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif msg.modulus == 16: 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auto_enrollment_response.expected_modulus = 16 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif msg.modulus == 32: 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) auto_enrollment_response.expected_modulus = 1 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response = dm.DeviceManagementResponse() 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response.auto_enrollment_response.CopyFrom(auto_enrollment_response) 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (200, response.SerializeToString()) 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def SetProtobufMessageField(self, group_message, field, field_value): 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '''Sets a field in a protobuf message. 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) group_message: The protobuf message. 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) field: The field of the message to set, it should be a member of 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) group_message.DESCRIPTOR.fields. 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) field_value: The value to set. 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ''' 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if field.label == field.LABEL_REPEATED: 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert type(field_value) == list 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) entries = group_message.__getattribute__(field.name) 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if field.message_type is None: 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for list_item in field_value: 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) entries.append(list_item) 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # This field is itself a protobuf. 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sub_type = field.message_type 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for sub_value in field_value: 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert type(sub_value) == dict 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Add a new sub-protobuf per list entry. 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sub_message = entries.add() 4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Now iterate over its fields and recursively add them. 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for sub_field in sub_message.DESCRIPTOR.fields: 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if sub_field.name in sub_value: 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) value = sub_value[sub_field.name] 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.SetProtobufMessageField(sub_message, sub_field, value) 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif field.type == field.TYPE_BOOL: 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert type(field_value) == bool 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif field.type == field.TYPE_STRING: 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert type(field_value) == str or type(field_value) == unicode 4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif field.type == field.TYPE_INT64: 4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert type(field_value) == int 4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif (field.type == field.TYPE_MESSAGE and 4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) field.message_type.name == 'StringList'): 4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert type(field_value) == list 4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) entries = group_message.__getattribute__(field.name).entries 4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for list_item in field_value: 4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) entries.append(list_item) 4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) raise Exception('Unknown field type %s' % field.type) 4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) group_message.__setattr__(field.name, field_value) 4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GatherDevicePolicySettings(self, settings, policies): 4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '''Copies all the policies from a dictionary into a protobuf of type 4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CloudDeviceSettingsProto. 4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) settings: The destination ChromeDeviceSettingsProto protobuf. 4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policies: The source dictionary containing policies in JSON format. 4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ''' 4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for group in settings.DESCRIPTOR.fields: 4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Create protobuf message for group. 4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) group_message = eval('dp.' + group.message_type.name + '()') 4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Indicates if at least one field was set in |group_message|. 4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) got_fields = False 4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Iterate over fields of the message and feed them from the 4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # policy config file. 4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for field in group_message.DESCRIPTOR.fields: 4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) field_value = None 4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if field.name in policies: 4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) got_fields = True 4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) field_value = policies[field.name] 4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.SetProtobufMessageField(group_message, field, field_value) 4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if got_fields: 4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) settings.__getattribute__(group.name).CopyFrom(group_message) 4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GatherUserPolicySettings(self, settings, policies): 4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '''Copies all the policies from a dictionary into a protobuf of type 4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) CloudPolicySettings. 4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) settings: The destination: a CloudPolicySettings protobuf. 4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policies: The source: a dictionary containing policies under keys 4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'recommended' and 'mandatory'. 4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ''' 4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for field in settings.DESCRIPTOR.fields: 5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # |field| is the entry for a specific policy in the top-level 5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # CloudPolicySettings proto. 5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Look for this policy's value in the mandatory or recommended dicts. 5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if field.name in policies.get('mandatory', {}): 5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mode = cp.PolicyOptions.MANDATORY 5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) value = policies['mandatory'][field.name] 5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) elif field.name in policies.get('recommended', {}): 5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mode = cp.PolicyOptions.RECOMMENDED 5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) value = policies['recommended'][field.name] 5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) else: 5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue 5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Create protobuf message for this policy. 5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_message = eval('cp.' + field.message_type.name + '()') 5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_message.policy_options.mode = mode 5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) field_descriptor = policy_message.DESCRIPTOR.fields_by_name['value'] 5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.SetProtobufMessageField(policy_message, field_descriptor, value) 5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) settings.__getattribute__(field.name).CopyFrom(policy_message) 5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def ProcessCloudPolicy(self, msg, token_info, response): 5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Handles a cloud policy request. (New protocol for policy requests.) 5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Encodes the policy into protobuf representation, signs it and constructs 5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) the response. 5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) msg: The CloudPolicyRequest message received from the client. 5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) token_info: the token extracted from the request. 5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response: A PolicyFetchResponse message that should be filled with the 5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response data. 5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if msg.machine_id: 5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.server.UpdateMachineId(token_info['device_token'], msg.machine_id) 5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Response is only given if the scope is specified in the config file. 5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Normally 'google/chromeos/device', 'google/chromeos/user' and 5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # 'google/chromeos/publicaccount' should be accepted. 5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy = self.server.GetPolicies() 5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy_value = '' 5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_key = msg.policy_type 5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if msg.settings_entity_id: 5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_key += '/' + msg.settings_entity_id 5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if msg.policy_type in token_info['allowed_policy_types']: 5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (msg.policy_type == 'google/chromeos/user' or 5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) msg.policy_type == 'google/chrome/user' or 5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) msg.policy_type == 'google/chromeos/publicaccount'): 5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) settings = cp.CloudPolicySettings() 5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) payload = self.server.ReadPolicyFromDataDir(policy_key, settings) 5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if payload is None: 5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.GatherUserPolicySettings(settings, policy.get(policy_key, {})) 5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) payload = settings.SerializeToString() 5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) elif dp is not None and msg.policy_type == 'google/chromeos/device': 5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) settings = dp.ChromeDeviceSettingsProto() 5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) payload = self.server.ReadPolicyFromDataDir(policy_key, settings) 5562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if payload is None: 5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.GatherDevicePolicySettings(settings, policy.get(policy_key, {})) 5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) payload = settings.SerializeToString() 5592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) elif msg.policy_type == 'google/chrome/extension': 5602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) settings = ep.ExternalPolicyData() 5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) payload = self.server.ReadPolicyFromDataDir(policy_key, settings) 5622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if payload is None: 5632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) payload = self.CreatePolicyForExternalPolicyData(policy_key) 5642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) else: 5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response.error_code = 400 5662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response.error_message = 'Invalid policy type' 5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return 5682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) else: 5692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response.error_code = 400 5702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response.error_message = 'Request not allowed for the token used' 5712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return 5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Sign with 'current_key_index', defaulting to key 0. 5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signing_key = None 5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) req_key = None 5762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) current_key_index = policy.get('current_key_index', 0) 5772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) nkeys = len(self.server.keys) 5782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (msg.signature_type == dm.PolicyFetchRequest.SHA1_RSA and 5792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) current_key_index in range(nkeys)): 5802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) signing_key = self.server.keys[current_key_index] 5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if msg.public_key_version in range(1, nkeys + 1): 5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # requested key exists, use for signing and rotate. 5832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) req_key = self.server.keys[msg.public_key_version - 1]['private_key'] 5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Fill the policy data protobuf. 5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy_data = dm.PolicyData() 5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy_data.policy_type = msg.policy_type 5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy_data.timestamp = int(time.time() * 1000) 5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy_data.request_token = token_info['device_token'] 5902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_data.policy_value = payload 5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy_data.machine_name = token_info['machine_name'] 5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy_data.valid_serial_number_missing = ( 5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token_info['machine_id'] in BAD_MACHINE_IDS) 5942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_data.settings_entity_id = msg.settings_entity_id 595bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch policy_data.service_account_identity = policy.get( 596bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 'service_account_identity', 597bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 'policy_testserver.py-service_account_identity') 5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if signing_key: 6002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_data.public_key_version = current_key_index + 1 6012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if msg.policy_type == 'google/chromeos/publicaccount': 6022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_data.username = msg.settings_entity_id 6032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) else: 6042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # For regular user/device policy, there is no way for the testserver to 6052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # know the user name belonging to the GAIA auth token we received (short 6062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # of actually talking to GAIA). To address this, we read the username from 6072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # the policy configuration dictionary, or use a default. 6082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_data.username = policy.get('policy_user', 'user@example.com') 6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy_data.device_id = token_info['device_id'] 6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signed_data = policy_data.SerializeToString() 6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response.policy_data = signed_data 6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if signing_key: 6142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response.policy_data_signature = ( 6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signing_key['private_key'].hashAndSign(signed_data).tostring()) 6162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if msg.public_key_version != current_key_index + 1: 6172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response.new_public_key = signing_key['public_key'] 6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if req_key: 6192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) response.new_public_key_signature = ( 6202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) req_key.hashAndSign(response.new_public_key).tostring()) 6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.DumpMessage('Response', response) 6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (200, response.SerializeToString()) 6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def CheckToken(self): 6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Helper for checking whether the client supplied a valid DM token. 6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Extracts the token from the request and passed to the server in order to 6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) look up the client. 6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A pair of token information record and error response. If the first 6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) element is None, then the second contains an error code to send back to 6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) the client. Otherwise the first element is the same structure that is 6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) returned by LookupToken(). 6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = 500 6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dmtoken = None 6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) request_device_id = self.GetUniqueParam('deviceid') 6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) match = re.match('GoogleDMToken token=(\\w+)', 6422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.headers.getheader('Authorization', '')) 6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if match: 6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dmtoken = match.group(1) 6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not dmtoken: 6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = 401 6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 6482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) token_info = self.server.LookupToken(dmtoken) 6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (not token_info or 6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not request_device_id or 6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token_info['device_id'] != request_device_id): 6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error = 410 6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (token_info, None) 6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('Token check failed with error %d' % error) 6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (None, (error, 'Server error %d' % error)) 6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def DumpMessage(self, label, msg): 6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Helper for logging an ASCII dump of a protobuf message.""" 6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) logging.debug('%s\n%s' % (label, str(msg))) 6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 6652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class PolicyTestServer(testserver_base.ClientRestrictingServerMixIn, 6662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) testserver_base.BrokenPipeHandlerMixIn, 6672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) testserver_base.StoppableHTTPServer): 6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Handles requests and keeps global service state.""" 6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def __init__(self, server_address, data_dir, policy_path, client_state_file, 6712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private_key_paths): 6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Initializes the server. 6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 6752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) server_address: Server host and port. 6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy_path: Names the file to read JSON-formatted policy from. 6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private_key_paths: List of paths to read private keys from. 6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 6792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) testserver_base.StoppableHTTPServer.__init__(self, server_address, 6802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) PolicyRequestHandler) 6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._registered_tokens = {} 6822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.data_dir = data_dir 6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.policy_path = policy_path 6842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.client_state_file = client_state_file 6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.keys = [] 6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if private_key_paths: 6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Load specified keys from the filesystem. 6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for key_path in private_key_paths: 6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 6912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) key_str = open(key_path).read() 6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except IOError: 6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print 'Failed to load private key from %s' % key_path 6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 6972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) key = tlslite.api.parsePEMKey(key_str, private=True) 6982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except SyntaxError: 6992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) key = tlslite.utils.Python_RSAKey.Python_RSAKey._parsePKCS8( 7002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) tlslite.utils.cryptomath.stringToBytes(key_str)) 7012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert key is not None 7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.keys.append({ 'private_key' : key }) 7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 7052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Generate 2 private keys if none were passed from the command line. 7062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for i in range(2): 7072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) key = tlslite.api.generateRSAKey(512) 7082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) assert key is not None 7092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.keys.append({ 'private_key' : key }) 7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Derive the public keys from the private keys. 7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for entry in self.keys: 7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) key = entry['private_key'] 7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) algorithm = asn1der.Sequence( 7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [ asn1der.Data(asn1der.OBJECT_IDENTIFIER, PKCS1_RSA_OID), 7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) asn1der.Data(asn1der.NULL, '') ]) 7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rsa_pubkey = asn1der.Sequence([ asn1der.Integer(key.n), 7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) asn1der.Integer(key.e) ]) 7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pubkey = asn1der.Sequence([ algorithm, asn1der.Bitstring(rsa_pubkey) ]) 7212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) entry['public_key'] = pubkey 7222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 7232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Load client state. 7242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if self.client_state_file is not None: 7252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 7262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) file_contents = open(self.client_state_file).read() 7272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self._registered_tokens = json.loads(file_contents) 7282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except IOError: 7292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pass 7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetPolicies(self): 7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Returns the policies to be used, reloaded form the backend file every 7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time this is called. 7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy = {} 7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if json is None: 7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print 'No JSON module, cannot parse policy information' 7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else : 7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) try: 7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) policy = json.loads(open(self.policy_path).read()) 7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) except IOError: 7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) print 'Failed to load policy from %s' % self.policy_path 7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return policy 7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def RegisterDevice(self, device_id, machine_id, type): 7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Registers a device or user and generates a DM token for it. 7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) device_id: The device identifier provided by the client. 7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) The newly generated device token for the device. 7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dmtoken_chars = [] 7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while len(dmtoken_chars) < 32: 7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dmtoken_chars.append(random.choice('0123456789abcdef')) 7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dmtoken = ''.join(dmtoken_chars) 7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) allowed_policy_types = { 7592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) dm.DeviceRegisterRequest.BROWSER: ['google/chrome/user'], 7602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) dm.DeviceRegisterRequest.USER: [ 7612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'google/chromeos/user', 7622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'google/chrome/extension' 7632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ], 7642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) dm.DeviceRegisterRequest.DEVICE: [ 7652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'google/chromeos/device', 7662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'google/chromeos/publicaccount' 7672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ], 7682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) dm.DeviceRegisterRequest.TT: ['google/chromeos/user', 7692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'google/chrome/user'], 7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if machine_id in KIOSK_MACHINE_IDS: 7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) enrollment_mode = dm.DeviceRegisterResponse.RETAIL 7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) enrollment_mode = dm.DeviceRegisterResponse.ENTERPRISE 7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._registered_tokens[dmtoken] = { 7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'device_id': device_id, 7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'device_token': dmtoken, 7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'allowed_policy_types': allowed_policy_types[type], 7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'machine_name': 'chromeos-' + machine_id, 7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'machine_id': machine_id, 7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'enrollment_mode': enrollment_mode, 7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 7832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.WriteClientState() 7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self._registered_tokens[dmtoken] 7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def UpdateMachineId(self, dmtoken, machine_id): 7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Updates the machine identifier for a registered device. 7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dmtoken: The device management token provided by the client. 7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) machine_id: Updated hardware identifier value. 7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if dmtoken in self._registered_tokens: 7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._registered_tokens[dmtoken]['machine_id'] = machine_id 7952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.WriteClientState() 7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def LookupToken(self, dmtoken): 7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Looks up a device or a user by DM token. 7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dmtoken: The device management token provided by the client. 8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) A dictionary with information about a device or user that is registered by 8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dmtoken, or None if the token is not found. 8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return self._registered_tokens.get(dmtoken, None) 8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def UnregisterDevice(self, dmtoken): 8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Unregisters a device identified by the given DM token. 8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dmtoken: The device management token provided by the client. 8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if dmtoken in self._registered_tokens.keys(): 8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) del self._registered_tokens[dmtoken] 8172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.WriteClientState() 8182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def WriteClientState(self): 8202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Writes the client state back to the file.""" 8212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if self.client_state_file is not None: 8222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) json_data = json.dumps(self._registered_tokens) 8232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) open(self.client_state_file, 'w').write(json_data) 8242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def GetBaseFilename(self, policy_selector): 8262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Returns the base filename for the given policy_selector. 8272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Args: 8292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_selector: the policy type and settings entity id, joined by '/'. 8302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Returns: 8322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) The filename corresponding to the policy_selector, without a file 8332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) extension. 8342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """ 8352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sanitized_policy_selector = re.sub('[^A-Za-z0-9.@-]', '_', policy_selector) 8362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return os.path.join(self.data_dir or '', 8372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'policy_%s' % sanitized_policy_selector) 8382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def ReadPolicyFromDataDir(self, policy_selector, proto_message): 8402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Tries to read policy payload from a file in the data directory. 8412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) First checks for a binary rendition of the policy protobuf in 8432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) <data_dir>/policy_<sanitized_policy_selector>.bin. If that exists, returns 8442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) it. If that file doesn't exist, tries 8452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) <data_dir>/policy_<sanitized_policy_selector>.txt and decodes that as a 8462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protobuf using proto_message. If that fails as well, returns None. 8472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Args: 8492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_selector: Selects which policy to read. 8502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) proto_message: Optional protobuf message object used for decoding the 8512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) proto text format. 8522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Returns: 8542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) The binary payload message, or None if not found. 8552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """ 8562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base_filename = self.GetBaseFilename(policy_selector) 8572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # Try the binary payload file first. 8592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 8602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return open(base_filename + '.bin').read() 8612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except IOError: 8622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pass 8632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) # If that fails, try the text version instead. 8652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if proto_message is None: 8662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return None 8672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 8692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) text = open(base_filename + '.txt').read() 8702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) google.protobuf.text_format.Merge(text, proto_message) 8712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return proto_message.SerializeToString() 8722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except IOError: 8732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return None 8742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except google.protobuf.text_format.ParseError: 8752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return None 8762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def ReadPolicyDataFromDataDir(self, policy_selector): 8782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """Returns the external policy data for |policy_selector| if found. 8792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Args: 8812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) policy_selector: Selects which policy to read. 8822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) Returns: 8842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) The data for the corresponding policy type and entity id, if found. 8852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) """ 8862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base_filename = self.GetBaseFilename(policy_selector) 8872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) try: 8882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return open(base_filename + '.data').read() 8892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) except IOError: 8902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return None 8912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class PolicyServerRunner(testserver_base.TestServerRunner): 8942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def __init__(self): 8962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) super(PolicyServerRunner, self).__init__() 8972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 8982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def create_server(self, server_data): 8992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) data_dir = self.options.data_dir or '' 9002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) config_file = (self.options.config_file or 9012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) os.path.join(data_dir, 'device_management')) 9022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) server = PolicyTestServer((self.options.host, self.options.port), 9032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) data_dir, config_file, 9042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.options.client_state_file, 9052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.options.policy_keys) 9062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) server_data['port'] = server.server_port 9072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return server 9082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 9092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def add_options(self): 9102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) testserver_base.TestServerRunner.add_options(self) 9112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.option_parser.add_option('--client-state', dest='client_state_file', 9122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) help='File that client state should be ' 9132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'persisted to. This allows the server to be ' 9142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'seeded by a list of pre-registered clients ' 9152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'and restarts without abandoning registered ' 9162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'clients.') 9172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.option_parser.add_option('--policy-key', action='append', 9182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) dest='policy_keys', 9192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) help='Specify a path to a PEM-encoded ' 9202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'private key to use for policy signing. May ' 9212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'be specified multiple times in order to ' 9222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'load multipe keys into the server. If the ' 9232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'server has multiple keys, it will rotate ' 9242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'through them in at each request in a ' 9252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'round-robin fashion. The server will ' 9262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'generate a random key if none is specified ' 9272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'on the command line.') 9282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.option_parser.add_option('--log-level', dest='log_level', 9292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) default='WARN', 9302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) help='Log level threshold to use.') 9312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) self.option_parser.add_option('--config-file', dest='config_file', 9322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) help='Specify a configuration file to use ' 9332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 'instead of the default ' 9342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) '<data_dir>/device_management') 9352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 9362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def run_server(self): 9372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) logger = logging.getLogger() 9382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) logger.setLevel(getattr(logging, str(self.options.log_level).upper())) 9392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (self.options.log_to_console): 9402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) logger.addHandler(logging.StreamHandler()) 9412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (self.options.log_file): 9422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) logger.addHandler(logging.FileHandler(self.options.log_file)) 9432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 9442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) testserver_base.TestServerRunner.run_server(self) 9452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 9462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 9472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)if __name__ == '__main__': 9482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) sys.exit(PolicyServerRunner().main()) 949