1/* 2 * Copyright (c) 2009-2010 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * spc.c - Security 26 * 27 * Created by Conrad Sauerwald on 1/9/09. 28 * 29 */ 30 31#include <TargetConditionals.h> 32#if TARGET_OS_EMBEDDED 33 34#include "SecurityCommands.h" 35 36#include <CoreFoundation/CoreFoundation.h> 37#include <AssertMacros.h> 38#include <TargetConditionals.h> 39#include <CFNetwork/CFNetwork.h> 40 41extern const CFStringRef kCFStreamPropertySSLPeerTrust; 42#include <Security/SecTrust.h> 43#include <Security/SecCertificate.h> 44#include <Security/SecCertificatePriv.h> 45#include <Security/SecImportExport.h> 46#include <Security/SecPolicy.h> 47#include <Security/SecIdentity.h> 48#include <Security/SecIdentityPriv.h> 49#include <Security/SecRSAKey.h> 50#include <Security/SecSCEP.h> 51#include <Security/SecCMS.h> 52#include <Security/SecItem.h> 53#include <Security/SecInternal.h> 54#include <utilities/array_size.h> 55#include <err.h> 56#include <stdio.h> 57#include <getopt.h> 58 59//static char *scep_url = "https://localhost:2000/cgi-bin/pkiclient.exe"; 60static char *user = "webuser"; 61static char *pass = "webpassowrd"; 62static int debug = 0; 63 64#define BUFSIZE 1024 65 66unsigned char device_cert_der[] = { 67 0x30, 0x82, 0x02, 0xdf, 0x30, 0x82, 0x02, 0x48, 0xa0, 0x03, 0x02, 0x01, 68 0x02, 0x02, 0x0a, 0x01, 0x5c, 0x0a, 0x2f, 0xfd, 0x20, 0x02, 0x00, 0xe6, 69 0x27, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 70 0x01, 0x0b, 0x05, 0x00, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 71 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 72 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 73 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 74 0x0b, 0x13, 0x0c, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x69, 0x50, 0x68, 75 0x6f, 0x6e, 0x65, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 76 0x13, 0x16, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x69, 0x50, 0x68, 0x6f, 77 0x6e, 0x65, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x43, 0x41, 78 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x31, 0x32, 0x31, 0x32, 0x32, 0x33, 79 0x30, 0x33, 0x35, 0x36, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x31, 0x32, 0x31, 80 0x32, 0x32, 0x33, 0x30, 0x33, 0x35, 0x36, 0x5a, 0x30, 0x81, 0x87, 0x31, 81 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28, 0x66, 0x31, 82 0x66, 0x35, 0x30, 0x66, 0x38, 0x31, 0x32, 0x32, 0x66, 0x62, 0x31, 0x30, 83 0x31, 0x34, 0x36, 0x66, 0x36, 0x35, 0x65, 0x39, 0x30, 0x33, 0x38, 0x30, 84 0x31, 0x37, 0x36, 0x33, 0x34, 0x32, 0x61, 0x64, 0x64, 0x36, 0x61, 0x38, 85 0x35, 0x63, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 86 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 87 0x13, 0x02, 0x43, 0x41, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 88 0x07, 0x13, 0x09, 0x43, 0x75, 0x70, 0x65, 0x72, 0x74, 0x69, 0x6e, 0x6f, 89 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 90 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x0f, 0x30, 91 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x06, 0x69, 0x50, 0x68, 0x6f, 92 0x6e, 0x65, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 93 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 94 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xba, 0xc5, 0x2a, 0x7b, 0xb3, 95 0x29, 0x21, 0x8d, 0xbd, 0x01, 0x12, 0xff, 0x0b, 0xf6, 0x14, 0x66, 0x15, 96 0x7b, 0x99, 0xf8, 0x1e, 0x08, 0x46, 0x40, 0xd7, 0x48, 0x57, 0xf0, 0x9c, 97 0x08, 0xac, 0xbc, 0x93, 0xc3, 0xd8, 0x81, 0xd8, 0x52, 0xd7, 0x9e, 0x9b, 98 0x3e, 0x65, 0xef, 0x98, 0x8d, 0x93, 0x2e, 0x79, 0x58, 0x22, 0x98, 0xe2, 99 0xd9, 0x2f, 0x84, 0x6d, 0xf0, 0x50, 0xc6, 0xba, 0x52, 0x28, 0x29, 0xf1, 100 0x83, 0x4c, 0xad, 0xdd, 0x5a, 0x7a, 0xab, 0xd2, 0x74, 0xb2, 0x1d, 0xc1, 101 0xa9, 0xe4, 0x87, 0xbd, 0xa0, 0x94, 0x0e, 0x50, 0xa7, 0xc7, 0xf1, 0x0d, 102 0xda, 0x0c, 0x36, 0x94, 0xf4, 0x2c, 0xb8, 0x76, 0xc7, 0x7b, 0x32, 0x87, 103 0x23, 0x70, 0x9f, 0x3f, 0x19, 0xeb, 0xc3, 0xb5, 0x3a, 0x6e, 0xec, 0xa7, 104 0x9c, 0xb4, 0xdb, 0xe7, 0x6e, 0x6d, 0x5e, 0x3c, 0x14, 0xa1, 0xa6, 0xfb, 105 0x50, 0xfb, 0x17, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x7e, 0x30, 0x7c, 106 0x30, 0x1b, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x14, 0xb2, 0xfe, 0x21, 107 0x23, 0x44, 0x86, 0x95, 0x6a, 0x79, 0xd5, 0x81, 0x26, 0x8e, 0x73, 0x10, 108 0xd8, 0xa7, 0x4c, 0x8e, 0x74, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 109 0x04, 0x16, 0x04, 0x14, 0x33, 0x2f, 0xbd, 0x38, 0x86, 0xbc, 0xb0, 0xb2, 110 0x6a, 0xd3, 0x86, 0xca, 0x92, 0x7c, 0x0a, 0x6d, 0x55, 0xab, 0x34, 0x81, 111 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 112 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 113 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x20, 0x06, 0x03, 0x55, 0x1d, 114 0x25, 0x01, 0x01, 0xff, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 115 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 116 0x05, 0x07, 0x03, 0x02, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 117 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0xe2, 118 0x0c, 0x37, 0x3e, 0x5f, 0x96, 0x10, 0x0d, 0xea, 0xa5, 0x15, 0x8f, 0xa2, 119 0x5b, 0x01, 0xe0, 0x1f, 0x15, 0x11, 0x42, 0x4b, 0x9e, 0x4b, 0x93, 0x90, 120 0x56, 0x4f, 0x4f, 0xbb, 0xc7, 0x32, 0x74, 0xfc, 0xbd, 0x1c, 0xcc, 0x06, 121 0x9f, 0xbb, 0x0b, 0x11, 0xa4, 0x8b, 0xa0, 0x52, 0x59, 0x94, 0xef, 0x87, 122 0x0c, 0xb1, 0xa8, 0xe0, 0x85, 0x96, 0xe1, 0x1b, 0x8b, 0x56, 0x12, 0x4d, 123 0x94, 0xd6, 0xa5, 0x52, 0x7f, 0x65, 0xf3, 0x21, 0x1f, 0xaa, 0x07, 0x8c, 124 0xaf, 0x69, 0xfa, 0x47, 0xbe, 0x3b, 0x74, 0x1a, 0x7c, 0xa6, 0x25, 0x63, 125 0x18, 0x5f, 0x0d, 0xda, 0xc4, 0x58, 0x01, 0xbc, 0xf2, 0x6d, 0x2d, 0xc1, 126 0x68, 0x3e, 0xa1, 0x2c, 0x59, 0x03, 0x3e, 0xa3, 0x13, 0x1b, 0xd9, 0x42, 127 0x28, 0x1e, 0x6c, 0x7f, 0x7f, 0x9d, 0x29, 0xf6, 0x43, 0x84, 0xe7, 0x60, 128 0xe3, 0x6f, 0x6a, 0x8a, 0x9f, 0x1d, 0x70 129}; 130unsigned int device_cert_der_len = 739; 131 132unsigned char device_key[] = { 133 0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xba, 134 0xc5, 0x2a, 0x7b, 0xb3, 0x29, 0x21, 0x8d, 0xbd, 0x01, 0x12, 0xff, 0x0b, 135 0xf6, 0x14, 0x66, 0x15, 0x7b, 0x99, 0xf8, 0x1e, 0x08, 0x46, 0x40, 0xd7, 136 0x48, 0x57, 0xf0, 0x9c, 0x08, 0xac, 0xbc, 0x93, 0xc3, 0xd8, 0x81, 0xd8, 137 0x52, 0xd7, 0x9e, 0x9b, 0x3e, 0x65, 0xef, 0x98, 0x8d, 0x93, 0x2e, 0x79, 138 0x58, 0x22, 0x98, 0xe2, 0xd9, 0x2f, 0x84, 0x6d, 0xf0, 0x50, 0xc6, 0xba, 139 0x52, 0x28, 0x29, 0xf1, 0x83, 0x4c, 0xad, 0xdd, 0x5a, 0x7a, 0xab, 0xd2, 140 0x74, 0xb2, 0x1d, 0xc1, 0xa9, 0xe4, 0x87, 0xbd, 0xa0, 0x94, 0x0e, 0x50, 141 0xa7, 0xc7, 0xf1, 0x0d, 0xda, 0x0c, 0x36, 0x94, 0xf4, 0x2c, 0xb8, 0x76, 142 0xc7, 0x7b, 0x32, 0x87, 0x23, 0x70, 0x9f, 0x3f, 0x19, 0xeb, 0xc3, 0xb5, 143 0x3a, 0x6e, 0xec, 0xa7, 0x9c, 0xb4, 0xdb, 0xe7, 0x6e, 0x6d, 0x5e, 0x3c, 144 0x14, 0xa1, 0xa6, 0xfb, 0x50, 0xfb, 0x17, 0x02, 0x03, 0x01, 0x00, 0x01, 145 0x02, 0x81, 0x80, 0x47, 0x9e, 0x91, 0xc2, 0xeb, 0x99, 0xeb, 0x26, 0xfa, 146 0x02, 0x2e, 0x71, 0xa4, 0xf9, 0x91, 0x2a, 0xf0, 0x33, 0xfc, 0x7f, 0xdb, 147 0xac, 0x5a, 0x9c, 0x44, 0xb1, 0x96, 0x1f, 0x4b, 0x06, 0x3c, 0x8e, 0xf7, 148 0xae, 0xd3, 0x18, 0x3f, 0x86, 0xcc, 0xee, 0x22, 0x23, 0xd4, 0x5d, 0x03, 149 0x47, 0xce, 0xd7, 0xb4, 0x6a, 0x6a, 0xa1, 0xeb, 0xe3, 0x52, 0xc8, 0x5a, 150 0x8c, 0x1b, 0xbd, 0x88, 0xf7, 0x36, 0x34, 0xd2, 0xfe, 0x6b, 0x75, 0x9c, 151 0xa4, 0x97, 0x2f, 0xeb, 0xa8, 0xec, 0x48, 0xb7, 0xe7, 0x53, 0x8f, 0xf1, 152 0x39, 0xca, 0xf8, 0xd2, 0xee, 0x34, 0xdc, 0x10, 0x99, 0x6f, 0xfd, 0x83, 153 0x21, 0xa9, 0xe0, 0xe9, 0x0c, 0x4e, 0x4e, 0x62, 0x5f, 0x9f, 0x0f, 0x05, 154 0xcd, 0xf4, 0x2b, 0x08, 0x06, 0x93, 0x55, 0x76, 0xaf, 0x63, 0x77, 0x80, 155 0x3e, 0xd5, 0xf2, 0xe0, 0x58, 0x7f, 0x3c, 0x41, 0x88, 0x4b, 0xc1, 0x02, 156 0x41, 0x01, 0x84, 0xfc, 0xa9, 0x26, 0x74, 0xe7, 0x4e, 0xe4, 0x39, 0xc4, 157 0x8f, 0x81, 0xe8, 0xc0, 0xd3, 0x6e, 0x01, 0x12, 0x7e, 0x12, 0x2d, 0x61, 158 0xaf, 0xe8, 0x60, 0x65, 0x8b, 0x50, 0x46, 0xdf, 0x02, 0x42, 0xe1, 0xea, 159 0xc1, 0x57, 0x9a, 0x2d, 0x90, 0xdc, 0x10, 0xf1, 0x79, 0xf0, 0xb5, 0x5c, 160 0x89, 0xef, 0x3b, 0xa8, 0x7a, 0x88, 0x3d, 0x54, 0xde, 0x35, 0x9c, 0xc3, 161 0x38, 0x4f, 0x2e, 0xb0, 0x74, 0xf7, 0x02, 0x40, 0x7a, 0xea, 0xc9, 0xfe, 162 0x48, 0xeb, 0x94, 0x20, 0x50, 0x33, 0x9d, 0x4f, 0x6f, 0x7f, 0xb1, 0x0e, 163 0xee, 0x42, 0x1c, 0xae, 0x72, 0x06, 0xb9, 0x9c, 0x80, 0x4a, 0xed, 0x07, 164 0xf8, 0x76, 0x5b, 0x37, 0x81, 0x45, 0x69, 0x09, 0xa5, 0x0d, 0x92, 0xed, 165 0xa8, 0xf0, 0x05, 0x6d, 0xb3, 0xa4, 0xef, 0xfb, 0x1b, 0xf0, 0x89, 0xea, 166 0x80, 0x2d, 0xaf, 0x9d, 0xbe, 0xc0, 0x08, 0x44, 0x58, 0x61, 0xc2, 0xe1, 167 0x02, 0x41, 0x01, 0x31, 0xaf, 0x14, 0x86, 0x82, 0x2c, 0x1c, 0x55, 0x42, 168 0x08, 0x73, 0xf6, 0x55, 0x20, 0xe3, 0x86, 0x79, 0x15, 0x3d, 0x39, 0xaf, 169 0xac, 0x2a, 0xfe, 0xe4, 0x72, 0x28, 0x2e, 0xe7, 0xe2, 0xec, 0xf5, 0xfe, 170 0x6f, 0xeb, 0x8c, 0x9a, 0x3e, 0xe0, 0xad, 0xf0, 0x2a, 0xb3, 0xf7, 0x33, 171 0xaf, 0x0b, 0x3e, 0x93, 0x95, 0x6c, 0xe5, 0x8f, 0xbd, 0x17, 0xfa, 0xed, 172 0xbc, 0x84, 0x8d, 0xc5, 0x55, 0x2a, 0x35, 0x02, 0x40, 0x6b, 0x4e, 0x1f, 173 0x4b, 0x03, 0x63, 0xcd, 0xab, 0xab, 0xf8, 0x73, 0x43, 0x8e, 0xa6, 0x1d, 174 0xef, 0x57, 0xe6, 0x95, 0x5d, 0x61, 0x24, 0x27, 0xd3, 0xcd, 0x58, 0x1b, 175 0xb7, 0x92, 0x9b, 0xd8, 0xa4, 0x0b, 0x11, 0x8a, 0x52, 0x26, 0x2a, 0x44, 176 0x73, 0x7f, 0xc1, 0x12, 0x2c, 0x23, 0xe1, 0x40, 0xb3, 0xaa, 0x3f, 0x82, 177 0x57, 0x1a, 0xd1, 0x47, 0x77, 0xe1, 0xb7, 0x89, 0x40, 0x09, 0x1c, 0x47, 178 0x61, 0x02, 0x41, 0x00, 0xa4, 0x47, 0x7d, 0x98, 0x74, 0x25, 0x95, 0x5a, 179 0xc9, 0xbe, 0x76, 0x66, 0xf8, 0x24, 0xb0, 0x83, 0x49, 0xd2, 0xce, 0x98, 180 0x75, 0x7c, 0xbb, 0xd9, 0x24, 0xe9, 0xc5, 0x53, 0xea, 0xd8, 0xec, 0x94, 181 0x79, 0xbb, 0x4e, 0x96, 0xc6, 0xd6, 0x17, 0x26, 0x77, 0xf3, 0x12, 0x3e, 182 0x15, 0x8e, 0x0c, 0x86, 0xdf, 0xa9, 0xf1, 0x34, 0x9b, 0x49, 0x28, 0x0a, 183 0x95, 0xb5, 0x29, 0x8f, 0x8a, 0x21, 0xff, 0xfc 184}; 185unsigned int device_key_len = 608; 186 187 188static inline CFTypeRef valid_cf_obj(CFTypeRef obj, CFTypeID type) 189{ 190 if (obj && CFGetTypeID(obj) == type) 191 return obj; 192 return NULL; 193} 194 195static inline CFTypeRef dict_get_value_of_type(CFDictionaryRef dict, CFTypeRef key, CFTypeID value_type) 196{ 197 CFTypeRef value = CFDictionaryGetValue(dict, key); 198 return valid_cf_obj(value, value_type); 199} 200 201#include <fcntl.h> 202static inline void write_data(const char * path, CFDataRef data) 203{ 204 int data_file = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0644); 205 write(data_file, CFDataGetBytePtr(data), CFDataGetLength(data)); 206 close(data_file); 207 printf("wrote intermediate result to: %s\n", path); 208} 209 210static void _query_string_apply(const void *key, const void *value, void *context) 211{ 212 CFStringRef escaped_key = 213 CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, 214 (CFStringRef)key, NULL, NULL, kCFStringEncodingUTF8); 215 CFStringRef escaped_value = 216 CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, 217 (CFStringRef)value, NULL, NULL, kCFStringEncodingUTF8); 218 CFMutableStringRef query_string = (CFMutableStringRef)context; 219 220 CFStringRef format; 221 if (CFStringGetLength(query_string) > 1) 222 format = CFSTR("&%@=%@"); 223 else 224 format = CFSTR("%@=%@"); 225 226 CFStringAppendFormat(query_string, NULL, format, escaped_key, escaped_value); 227 CFRelease(escaped_key); 228 CFRelease(escaped_value); 229} 230 231static CFURLRef build_url(CFStringRef base, CFDictionaryRef info) 232{ 233 CFURLRef url = NULL, base_url = NULL; 234 CFMutableStringRef query_string = 235 CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("?")); 236 require(query_string, out); 237 if (info) 238 CFDictionaryApplyFunction(info, _query_string_apply, query_string); 239 base_url = CFURLCreateWithString(kCFAllocatorDefault, base, NULL); 240 url = CFURLCreateWithString(kCFAllocatorDefault, query_string, base_url); 241out: 242 CFReleaseSafe(base_url); 243 return url; 244} 245 246static CFURLRef scep_url_operation(CFStringRef base, CFStringRef operation, CFStringRef message) 247{ 248 CFURLRef url = NULL; 249 const void *keys[] = { CFSTR("operation"), CFSTR("message") }; 250 const void *values[] = { operation, message }; 251 252 require(operation, out); 253 CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values, message ? 2 : 1, NULL, NULL); 254 url = build_url(base, dict); 255 CFRelease(dict); 256out: 257 return url; 258} 259 260static bool auth_failed(CFHTTPMessageRef request, CFReadStreamRef readStream) 261{ 262 bool isAuthenticationChallenge = false; 263 CFHTTPMessageRef responseHeaders = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader); 264 if (responseHeaders) { 265 // Is the server response an challenge for credentials? 266 isAuthenticationChallenge = (CFHTTPMessageGetResponseStatusCode(responseHeaders) == 401); 267 if (isAuthenticationChallenge) { 268 CFStringRef cf_user = CFStringCreateWithCString(kCFAllocatorDefault, user, kCFStringEncodingUTF8); 269 CFStringRef cf_pass = CFStringCreateWithCString(kCFAllocatorDefault, pass, kCFStringEncodingUTF8); 270 CFHTTPMessageAddAuthentication(request, responseHeaders, cf_user, cf_pass, NULL, false); 271 } 272 CFRelease(responseHeaders); 273 } 274 return isAuthenticationChallenge; 275} 276 277static CFHTTPMessageRef load_request(CFHTTPMessageRef request, CFMutableDataRef data, int retry) 278{ 279 CFHTTPMessageRef result = NULL; 280 281 if (retry < 0) 282 return result; 283 284 CFReadStreamRef rs; 285 rs = CFReadStreamCreateForHTTPRequest(NULL, request); 286 287 const void *keys[] = { 288 kCFStreamSSLValidatesCertificateChain, 289// kCFStreamSSLCertificates, 290 }; 291 //CFArrayRef ident = mit_identity(); 292 const void *values[] = { 293 kCFBooleanFalse, 294// ident, 295 }; 296 297 CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values, 298 array_size(keys), 299 &kCFTypeDictionaryKeyCallBacks, 300 &kCFTypeDictionaryValueCallBacks); 301 CFReadStreamSetProperty(rs, kCFStreamPropertySSLSettings, dict); 302 CFRelease(dict); 303 CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue); 304 305 if (CFReadStreamOpen(rs)) { 306 do { 307 if (auth_failed(request, rs)) { 308 CFReadStreamClose(rs); 309 CFDataSetLength(data, 0); 310 if (retry) 311 return load_request(request, data, retry - 1); 312 break; 313 } 314 UInt8 buf[BUFSIZE]; 315 CFIndex bytesRead = CFReadStreamRead(rs, buf, BUFSIZE); 316 if (bytesRead > 0) { 317 CFDataAppendBytes(data, buf, bytesRead); 318 } else if (bytesRead == 0) { 319 // Success 320 result = (CFHTTPMessageRef)CFReadStreamCopyProperty(rs, kCFStreamPropertyHTTPResponseHeader); 321 break; 322 } else { 323 // Error 324 break; 325 } 326 } while (true); 327 } else { 328 // Error 329 } 330 331 CFReadStreamClose(rs); 332 CFRelease(rs); 333 if (debug) 334 CFShow(result); 335 return result; 336} 337 338 339static CFDictionaryRef get_encrypted_profile_packet(CFStringRef base_url) 340{ 341#if 1 342 SecCertificateRef cert = SecCertificateCreateWithBytes(NULL, 343 device_cert_der, device_cert_der_len); 344 SecKeyRef key = SecKeyCreateRSAPrivateKey(NULL, 345 device_key, device_key_len, kSecKeyEncodingPkcs1); 346 SecIdentityRef identity = SecIdentityCreate(kCFAllocatorDefault, cert, key); 347 CFRelease(cert); 348 CFRelease(key); 349#else 350 SecIdentityRef identity = lockdown_copy_device_identity(); 351#endif 352 353 CFMutableDictionaryRef machine_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 354 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 355 CFDictionarySetValue(machine_dict, CFSTR("UDID"), CFSTR("f1f50f8122fb10146f65e90380176342add6a85c")); 356 CFStringRef cf_user = CFStringCreateWithCString(kCFAllocatorDefault, user, kCFStringEncodingUTF8); 357 if (cf_user) { 358 CFDictionarySetValue(machine_dict, CFSTR("USERNAME"), cf_user); 359 CFRelease(cf_user); 360 } 361 CFStringRef cf_pass = CFStringCreateWithCString(kCFAllocatorDefault, pass, kCFStringEncodingUTF8); 362 if (cf_pass) { 363 CFDictionarySetValue(machine_dict, CFSTR("PASSWORD"), cf_pass); 364 CFRelease(cf_pass); 365 } 366 CFDataRef machine_dict_data = CFPropertyListCreateXMLData(kCFAllocatorDefault, 367 (CFPropertyListRef)machine_dict); 368 CFMutableDataRef signed_data = CFDataCreateMutable(kCFAllocatorDefault, 0); 369 if (SecCMSSignDataAndAttributes(identity, machine_dict_data, false, signed_data, NULL)) 370 errx(1, "failed to sign data"); 371 CFRelease(identity); 372 CFRelease(machine_dict_data); 373 CFRelease(machine_dict); 374 375 CFURLRef url = build_url(base_url, NULL); 376 CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, 377 CFSTR("POST"), url, kCFHTTPVersion1_1); 378 CFHTTPMessageSetBody(request, signed_data); 379 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), 380 CFSTR("application/pkcs7-signature")); 381 CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); 382 CFHTTPMessageRef response = load_request(request, data, 1); 383 CFRelease(request); 384 CFRelease(response); 385 CFErrorRef error = NULL; 386 CFTypeRef result = CFPropertyListCreateWithData(kCFAllocatorDefault, 387 data, kCFPropertyListImmutable, NULL, &error); 388 CFRelease(data); 389 if (error) { 390 CFShow(error); 391 errx(1, "failed to decode encrypted profile response"); 392 CFRelease(error); 393 } 394 if (CFGetTypeID(result) != CFDictionaryGetTypeID()) 395 CFReleaseNull(result); 396 return (CFDictionaryRef)result; 397} 398 399static SecCertificateRef get_ca_cert(CFStringRef scep_base_url, CFStringRef scep_ca_name) 400{ 401 SecCertificateRef cert = NULL; 402 CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); 403 404 CFURLRef url = scep_url_operation(scep_base_url, CFSTR("GetCACert"), scep_ca_name); 405 CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, 406 CFSTR("GET"), url, kCFHTTPVersion1_1); 407 CFHTTPMessageRef result = load_request(request, data, 1); 408 409 if (!result) 410 return NULL; 411 CFStringRef contentTypeValue = CFHTTPMessageCopyHeaderFieldValue(result, CFSTR("Content-Type")); 412// CFHTTPMessageRef response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false); 413// CFHTTPMessageAppendBytes(response, CFDataGetBytePtr(data), CFDataGetLength(data)); 414// CFDataRef bodyValue = CFHTTPMessageCopyBody(response); 415 if (kCFCompareEqualTo == CFStringCompare(CFSTR("application/x-x509-ca-cert"), 416 contentTypeValue, kCFCompareCaseInsensitive)) { 417 // CA only response: application/x-x509-ca-cert 418 cert = SecCertificateCreateWithData(kCFAllocatorDefault, data); 419 if (debug) 420 write_data("/tmp/ca_cert.der", data); 421 } else if (kCFCompareEqualTo == CFStringCompare(CFSTR("application/x-x509-ca-ra-cert"), 422 contentTypeValue, kCFCompareCaseInsensitive)) { 423 // RA/CA response: application/x-x509-ca-ra-cert 424 CFArrayRef cert_array = SecCMSCertificatesOnlyMessageCopyCertificates(data); 425 if (debug) 426 write_data("/tmp/ca_cert_array.der", data); 427 cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_array, 0); 428 CFRetain(cert); 429 CFRelease(cert_array); 430 } 431 CFRelease(contentTypeValue); 432 CFRelease(data); 433 return cert; 434} 435 436 437static SecIdentityRef perform_scep(CFDictionaryRef scep_dict) 438{ 439 SecIdentityRef identity = NULL; 440 CFDictionaryRef parameters = NULL, csr_parameters = NULL; 441 CFStringRef scep_base_url = NULL, scep_instance_name = NULL, 442 scep_challenge = NULL, scep_subject = NULL, scep_subject_requested = NULL; 443 CFTypeRef scep_key_bitsize = NULL; 444 const void *keygen_keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits }; 445 const void *keygen_vals[] = { kSecAttrKeyTypeRSA, CFSTR("512") }; 446 447 require(valid_cf_obj(scep_dict,CFDictionaryGetTypeID()), out); 448 require(scep_base_url = 449 dict_get_value_of_type(scep_dict, CFSTR("URL"), CFStringGetTypeID()), out); 450 require(scep_instance_name = 451 dict_get_value_of_type(scep_dict, CFSTR("NAME"), CFStringGetTypeID()), out); 452 require(scep_challenge = 453 dict_get_value_of_type(scep_dict, CFSTR("CHALLENGE"), CFStringGetTypeID()), out); 454 455 scep_subject_requested = 456 dict_get_value_of_type(scep_dict, CFSTR("SUBJECT"), CFStringGetTypeID()); 457 if (scep_subject_requested) { 458 CFArrayRef scep_subject_components = 459 CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, 460 scep_subject_requested, CFSTR("=")); 461 if (CFArrayGetCount(scep_subject_components) == 2) 462 scep_subject = CFArrayGetValueAtIndex(scep_subject_components, 1); 463 } 464 465 scep_key_bitsize = 466 dict_get_value_of_type(scep_dict, CFSTR("KEYSIZE"), CFNumberGetTypeID()); 467 if (!scep_key_bitsize) 468 scep_key_bitsize = 469 dict_get_value_of_type(scep_dict, CFSTR("KEYSIZE"), CFStringGetTypeID()); 470 if (scep_key_bitsize) 471 keygen_vals[1] = scep_key_bitsize; 472 473 parameters = CFDictionaryCreate(kCFAllocatorDefault, 474 keygen_keys, keygen_vals, array_size(keygen_vals), 475 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); 476 477 int key_usage = kSecKeyUsageDigitalSignature | kSecKeyUsageKeyEncipherment; 478 CFNumberRef key_usage_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_usage); 479 const void *key[] = { kSecCSRChallengePassword, kSecCertificateKeyUsage }; 480 const void *val[] = { scep_challenge ? scep_challenge : CFSTR("magic"), key_usage_num }; 481 csr_parameters = CFDictionaryCreate(kCFAllocatorDefault, 482 key, val, array_size(key), NULL, NULL); 483 484 SecCertificateRef ca_cert = get_ca_cert(scep_base_url, scep_instance_name ? scep_instance_name : CFSTR("test")); 485 if (!ca_cert) 486 errx(1, "no ca cert returned from scep server."); 487 488 identity = NULL; // enroll_scep(scep_base_url, scep_subject, csr_parameters, parameters, ca_cert); 489 if (!identity) 490 errx(1, "failed to get identity from scep server."); 491 492out: 493 CFReleaseSafe(parameters); 494 return identity; 495} 496 497static CFDataRef get_profile(CFStringRef url_cfstring, SecIdentityRef identity) 498{ 499 CFURLRef url = NULL; 500 CFHTTPMessageRef request = NULL; 501 SecCertificateRef cert = NULL; 502 CFDataRef cert_data = NULL; 503 CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0); 504 CFHTTPMessageRef response = NULL; 505 506 require(url = CFURLCreateWithString(kCFAllocatorDefault, url_cfstring, NULL), out); 507 require(request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, 508 CFSTR("POST"), url, kCFHTTPVersion1_1), out); 509 require_noerr(SecIdentityCopyCertificate(identity, &cert), out); 510 require(cert_data = SecCertificateCopyData(cert), out); 511 CFHTTPMessageSetBody(request, cert_data); 512 // this is technically the wrong mimetype; we'll probably switch to signed data 513 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), CFSTR("application/x-x509-ca-cert")); 514 response = load_request(request, data, 1); 515 if (debug && data) 516 CFShow(data); 517out: 518 //CFReleaseSafe(response); 519 CFReleaseSafe(request); 520 CFReleaseSafe(url); 521 CFReleaseSafe(cert_data); 522 CFReleaseSafe(cert); 523 CFReleaseSafe(response); 524 return data; 525 526} 527 528static bool validate_profile(CFDataRef profile_plist_data, SecIdentityRef identity) 529{ 530 CFStringRef type = NULL, uuid = NULL; 531 CFDataRef enc_payload = NULL; 532 CFTypeRef result = CFPropertyListCreateWithData(kCFAllocatorDefault, 533 profile_plist_data, kCFPropertyListImmutable, NULL, NULL); 534 require(valid_cf_obj(result, CFDictionaryGetTypeID()), out); 535 require(type = dict_get_value_of_type(result, CFSTR("PayloadType"), 536 CFStringGetTypeID()), out); 537 require(uuid = dict_get_value_of_type(result, CFSTR("PayloadUUID"), 538 CFStringGetTypeID()), out); 539 enc_payload = dict_get_value_of_type(result, CFSTR("EncryptedPayloadContent"), 540 CFDataGetTypeID()); 541 if (enc_payload) { 542 CFMutableDataRef dec_data = CFDataCreateMutable(kCFAllocatorDefault, 0); 543 require_noerr(SecCMSDecryptEnvelopedData(enc_payload, dec_data, NULL), out); 544 bool sub = validate_profile(dec_data, identity); 545 CFReleaseSafe(dec_data); 546 return sub; 547 } else { 548 if (debug) 549 write_data("/tmp/unencrypted_profile.mobileconfig", profile_plist_data); 550 //CFDictionaryRef sub_conf = dict_get_value_of_type(result, CFSTR("PayloadContent"), CFDictionaryGetTypeID()); 551 } 552 return true; 553out: 554 return false; 555} 556 557#if 0 558static const char *auth = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\ 559<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/\n\ 560PropertyList-1.0.dtd\">\n\ 561<plist version=\"1.0\">\n\ 562<dict>\n\ 563 <key>PayloadContent</key>\n\ 564 <dict>\n\ 565 <key>URL</key>\n\ 566 <string>https://phone.vpn.apple.com/deviceid/</string>\n\ 567 <key>DeviceAttributes</key>\n\ 568 <array>\n\ 569 <string>UDID</string>\n\ 570 <string>VERSION</string>\n\ 571 <string>MAC_ADDRESS_EN0</string>\n\ 572 <string>MAC_ADDRESS_IP0</string>\n\ 573 </array>\n\ 574 <key>CHALLENGE</key>\n\ 575 <string>${USER_COOKIE}</string>\n\ 576 </dict>\n\ 577 <key>PayloadType</key>\n\ 578 <string>Device Identification</string>\n\ 579</dict>\n\ 580</plist>\n"; 581 582static void annotate_machine_info(const void *value, void *context) 583{ 584 CFDictionaryRef machine_dict = NULL; 585 CFStringRef machine_key = NULL; 586 require(machine_dict = (CFDictionaryRef)valid_cf_obj(context, 587 CFDictionaryGetTypeID()), out); 588 require(machine_key = (CFStringRef)valid_cf_obj((CFTypeRef)value, 589 CFStringGetTypeID()), out); 590 if (CFEqual(machine_key, CFSTR("UDID"))) { 591 592 } 593out: 594 return; 595} 596 597static void machine_authentication(CFDataRef req, CFDataRef reply) 598{ 599 CFDataRef machine_auth_request = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 600 (uint8_t*)auth, strlen(auth), kCFAllocatorNull); 601 CFDictionaryRef auth_dict = NULL; 602 CFMutableDictionaryRef auth_reply_dict = 603 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 604 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 605 606 if (req) { CFRelease(machine_auth_request); machine_auth_request = req; CFRetain(req); } 607 CFDictionaryRef auth_request_dict = CFPropertyListCreateWithData(kCFAllocatorDefault, 608 machine_auth_request, kCFPropertyListImmutable, NULL, NULL); 609 610 require(auth_reply_dict, out); 611 require(valid_cf_obj(auth_request_dict, CFDictionaryGetTypeID()), out); 612 require(auth_dict = dict_get_value_of_type(auth_request_dict, CFSTR("PayloadContent"), CFDictionaryGetTypeID()), out); 613 CFStringRef challenge = dict_get_value_of_type(auth_dict, CFSTR("CHALLENGE"), CFStringGetTypeID()); 614 if (challenge) 615 CFDictionarySetValue(auth_reply_dict, CFSTR("CHALLENGE"), challenge); 616 CFArrayRef device_info = dict_get_value_of_type(auth_dict, CFSTR("DeviceAttributes"), CFArrayGetTypeID()); 617 if (device_info) 618 CFArrayApplyFunction(device_info, CFRangeMake(0, CFArrayGetCount(device_info)), 619 annotate_machine_info, auth_reply_dict); 620 621 // make copy of reply dict 622out: 623 CFReleaseSafe(machine_auth_request); 624 CFReleaseSafe(auth_request_dict); 625} 626#endif 627 628extern int command_spc(int argc, char * const *argv) 629{ 630 SecIdentityRef identity = NULL; 631 extern char *optarg; 632 extern int optind; 633 int arg; 634 while ((arg = getopt(argc, argv, "du:p:")) != -1) { 635 switch (arg) { 636 case 'd': 637 debug = 1; 638 break; 639 case 'u': 640 user = optarg; 641 break; 642 case 'p': 643 pass = optarg; 644 break; 645 case 'h': 646 default: 647 return 2; 648 } 649 } 650 651 argc -= optind; 652 argv += optind; 653 654#if 0 655 if (argc == 1) { 656 // get plist from argv[0] url 657 } else if (argc == 0) { 658 machine_authentication(NULL, NULL); 659 } else return 2; 660#endif 661 662 if (argc != 1) 663 return 2; 664 665 CFStringRef machine_id_url_cfstring = CFStringCreateWithCString(kCFAllocatorDefault, 666 argv[0], kCFStringEncodingUTF8); 667 668 CFDictionaryRef enroll_packet = 669 get_encrypted_profile_packet(machine_id_url_cfstring); 670 require(enroll_packet && CFGetTypeID(enroll_packet) == CFDictionaryGetTypeID(), out); 671 //require(payload_type(enroll_packet, "Encrypted Profile Service"), out); 672 CFDictionaryRef scep_config = NULL; 673 CFDictionaryRef payload_content = NULL; 674 require(payload_content = dict_get_value_of_type(enroll_packet, 675 CFSTR("PayloadContent"), CFDictionaryGetTypeID()), out); 676 require(scep_config = dict_get_value_of_type(payload_content, 677 CFSTR("SCEP"), CFDictionaryGetTypeID()), out); 678 require(identity = perform_scep(scep_config), out); 679 CFDictionaryRef dict = CFDictionaryCreate(NULL, &kSecValueRef, (const void **)&identity, 1, NULL, NULL); 680 require_noerr(SecItemAdd(dict, NULL), out); 681 CFReleaseSafe(dict); 682 CFStringRef profile_url = NULL; 683 require(profile_url = dict_get_value_of_type(payload_content, 684 CFSTR("URL"), CFStringGetTypeID()), out); 685 686 CFDataRef profile_data = NULL; 687 require(profile_data = get_profile(profile_url, identity), out); 688 CFDataRef profile_plist = NULL; 689 if (debug) 690 write_data("/tmp/profile.mobileconfig", profile_data); 691 692 require_noerr(SecCMSVerify(profile_data, NULL, NULL, NULL, &profile_plist), out); 693 CFRelease(profile_data); 694 require(profile_plist, out); 695 require(validate_profile(profile_plist, identity), out); 696 697 return 0; 698out: 699 errx(1, "fail."); 700 return -1; 701} 702 703 704#endif // TARGET_OS_EMBEDDED 705