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