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