summaryrefslogtreecommitdiffstats
path: root/lockdown.c
diff options
context:
space:
mode:
Diffstat (limited to 'lockdown.c')
-rw-r--r--lockdown.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/lockdown.c b/lockdown.c
new file mode 100644
index 0000000..5ca6001
--- /dev/null
+++ b/lockdown.c
@@ -0,0 +1,349 @@
1/*
2 * lockdown.c -- libiphone built-in lockdownd client
3 * Written by FxChiP
4 */
5
6#include "usbmux.h"
7#include "iphone.h"
8#include "lockdown.h"
9#include <errno.h>
10#include <string.h>
11
12extern int debug;
13
14lockdownd_client *new_lockdownd_client(iPhone *phone) {
15 if (!phone) return NULL;
16 lockdownd_client *control = (lockdownd_client*)malloc(sizeof(lockdownd_client));
17 control->connection = mux_connect(phone, 0x0a00, 0xf27e);
18 if (!control->connection) {
19 free(control);
20 return NULL;
21 }
22
23 control->ssl_session = (gnutls_session_t*)malloc(sizeof(gnutls_session_t));
24 control->in_SSL = 0;
25 control->iphone = phone;
26 control->gtls_buffer_hack_len = 0;
27 return control;
28}
29
30void lockdown_close(lockdownd_client *control) {
31 if (!control) return;
32 if (control->connection) {
33 mux_close_connection(control->iphone, control->connection);
34 }
35
36 if (control->ssl_session) free(control->ssl_session);
37 free(control);
38}
39
40
41int lockdownd_recv(lockdownd_client *control, char **dump_data) {
42 char *receive;
43 uint32 datalen = 0, bytes = 0;
44
45 if (!control->in_SSL) bytes = mux_recv(control->iphone, control->connection, &datalen, sizeof(datalen));
46 else bytes = gnutls_record_recv(*control->ssl_session, &datalen, sizeof(datalen));
47 datalen = ntohl(datalen);
48
49 receive = (char*)malloc(sizeof(char) * datalen);
50 if (!control->in_SSL) bytes = mux_recv(control->iphone, control->connection, receive, datalen);
51 else bytes = gnutls_record_recv(*control->ssl_session, receive, datalen);
52 *dump_data = receive;
53 return bytes;
54}
55
56int lockdownd_send(lockdownd_client *control, char *raw_data, uint32 length) {
57 char *real_query;
58 int bytes;
59
60 real_query = (char*)malloc(sizeof(char) * (length+4));
61 length = htonl(length);
62 memcpy(real_query, &length, sizeof(length));
63 memcpy(real_query+4, raw_data, ntohl(length));
64 if (!control->in_SSL) bytes = mux_send(control->iphone, control->connection, real_query, ntohl(length)+sizeof(length));
65 else gnutls_record_send(*control->ssl_session, real_query, ntohl(length)+sizeof(length));
66 return bytes;
67}
68
69int lockdownd_hello(lockdownd_client *control) {
70 xmlDocPtr plist = new_plist();
71 xmlNode *dict, *key;
72 char **dictionary;
73 int bytes = 0, i = 0;
74
75 dict = add_child_to_plist(plist, "dict", "\n", NULL, 0);
76 key = add_key_str_dict_element(plist, dict, "Request", "QueryType", 1);
77 char *XML_content;
78 uint32 length;
79
80 xmlDocDumpMemory(plist, &XML_content, &length);
81
82 bytes = lockdownd_send(control, XML_content, length);
83
84 xmlFree(XML_content);
85 xmlFreeDoc(plist); plist = NULL;
86
87 bytes = lockdownd_recv(control, &XML_content);
88
89 plist = xmlReadMemory(XML_content, bytes, NULL, NULL, 0);
90 if (!plist) return 0;
91 dict = xmlDocGetRootElement(plist);
92 for (dict = dict->children; dict; dict = dict->next) {
93 if (!xmlStrcmp(dict->name, "dict")) break;
94 }
95 if (!dict) return 0;
96
97 dictionary = read_dict_element_strings(dict);
98 xmlFreeDoc(plist);
99 free(XML_content);
100
101 for (i = 0; strcmp(dictionary[i], ""); i+=2) {
102 if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) {
103 free_dictionary(dictionary);
104 return 1;
105 }
106 }
107
108 free_dictionary(dictionary);
109 return 0;
110}
111
112int lockdownd_start_SSL_session(lockdownd_client *control, const char *HostID) {
113 xmlDocPtr plist = new_plist();
114 xmlNode *dict = add_child_to_plist(plist, "dict", "\n", NULL, 0);
115 xmlNode *key;
116 char *what2send = NULL, **dictionary = NULL;
117 uint32 len = 0, bytes = 0, return_me = 0, i = 0;
118 // end variables
119
120 key = add_key_str_dict_element(plist, dict, "HostID", HostID, 1);
121 if (!key) {
122 if (debug) printf("Couldn't add a key.\n");
123 xmlFreeDoc(plist);
124 return 0;
125 }
126 key = add_key_str_dict_element(plist, dict, "Request", "StartSession", 1);
127 if (!key) {
128 if (debug) printf("Couldn't add a key.\n");
129 xmlFreeDoc(plist);
130 return 0;
131 }
132
133 xmlDocDumpMemory(plist, &what2send, &len);
134 bytes = lockdownd_send(control, what2send, len);
135
136 xmlFree(what2send);
137 xmlFreeDoc(plist);
138
139 if (bytes > 0) {
140 len = lockdownd_recv(control, &what2send);
141 plist = xmlReadMemory(what2send, len, NULL, NULL, 0);
142 dict = xmlDocGetRootElement(plist);
143 for (dict = dict->children; dict; dict = dict->next) {
144 if (!xmlStrcmp(dict->name, "dict")) break;
145 }
146 dictionary = read_dict_element_strings(dict);
147 xmlFreeDoc(plist);
148 free(what2send);
149 for (i = 0; strcmp(dictionary[i], ""); i+=2) {
150 if (!strcmp(dictionary[i], "Result") && !strcmp(dictionary[i+1], "Success")) {
151 // Set up GnuTLS...
152 gnutls_certificate_credentials_t xcred;
153
154 if (debug) printf("We started the session OK, now trying GnuTLS\n");
155 errno = 0;
156 gnutls_global_init();
157 gnutls_certificate_allocate_credentials(&xcred);
158 gnutls_certificate_set_x509_trust_file(xcred, "hostcert.pem", GNUTLS_X509_FMT_PEM);
159 gnutls_init(control->ssl_session, GNUTLS_CLIENT);
160 if ((return_me = gnutls_priority_set_direct(*control->ssl_session, "NORMAL:+VERS-SSL3.0", NULL)) < 0) {
161 printf("oops? bad options?\n");
162 gnutls_perror(return_me);
163 return 0;
164 }
165 gnutls_credentials_set(*control->ssl_session, GNUTLS_CRD_CERTIFICATE, xcred); // this part is killing me.
166
167 if (debug) printf("GnuTLS step 1...\n");
168 gnutls_transport_set_ptr(*control->ssl_session, (gnutls_transport_ptr_t) control);
169 if (debug) printf("GnuTLS step 2...\n");
170 gnutls_transport_set_push_function(*control->ssl_session, (gnutls_push_func)&lockdownd_secuwrite);
171 if (debug) printf("GnuTLS step 3...\n");
172 gnutls_transport_set_pull_function(*control->ssl_session, (gnutls_pull_func)&lockdownd_securead);
173 if (debug) printf("GnuTLS step 4 -- now handshaking...\n");
174
175 if (errno && debug) printf("WARN: errno says %s before handshake!\n", strerror(errno));
176 return_me = gnutls_handshake(*control->ssl_session);
177 if (debug) printf("GnuTLS handshake done...\n");
178
179 free_dictionary(dictionary);
180
181 if (return_me != GNUTLS_E_SUCCESS) {
182 if (debug) printf("GnuTLS reported something wrong.\n");
183 gnutls_perror(return_me);
184 if (debug) printf("oh.. errno says %s\n", strerror(errno));
185 return 0;
186 } else {
187 control->in_SSL = 1;
188 return 1;
189 }
190 }
191 }
192
193 if (debug) {
194 printf("Apparently failed negotiating with lockdownd.\n");
195 printf("Responding dictionary: \n");
196 for (i = 0; strcmp(dictionary[i], ""); i+=2) {
197 printf("\t%s: %s\n", dictionary[i], dictionary[i+1]);
198 }
199 }
200
201 free_dictionary(dictionary);
202 return 0;
203 } else {
204 if (debug) printf("Didn't get enough bytes.\n");
205 return 0;
206 }
207}
208
209ssize_t lockdownd_secuwrite(gnutls_transport_ptr_t transport, char *buffer, size_t length) {
210 int bytes = 0;
211 lockdownd_client *control;
212 control = (lockdownd_client*)transport;
213 if (debug) printf("lockdownd_secuwrite() called\n");
214 if (debug) printf("pre-send\nlength = %i\n", length);
215 bytes = mux_send(control->iphone, control->connection, buffer, length);
216 if (debug) printf("post-send\nsent %i bytes\n", bytes);
217 return bytes;
218}
219
220ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_t length) {
221 int bytes = 0, pos_start_fill = 0;
222 char *hackhackhack = NULL;
223 lockdownd_client *control;
224 control = (lockdownd_client*)transport;
225 if (debug) printf("lockdownd_securead() called\nlength = %i\n", length);
226 // Buffering hack! Throw what we've got in our "buffer" into the stream first, then get more.
227 if (control->gtls_buffer_hack_len > 0) {
228 if (length > control->gtls_buffer_hack_len) { // If it's asking for more than we got
229 length -= control->gtls_buffer_hack_len; // Subtract what we have from their requested length
230 pos_start_fill = control->gtls_buffer_hack_len; // set the pos to start filling at
231 memcpy(buffer, control->gtls_buffer_hack, control->gtls_buffer_hack_len); // Fill their buffer partially
232 free(control->gtls_buffer_hack); // free our memory, it's not chained anymore
233 control->gtls_buffer_hack_len = 0; // we don't have a hack buffer anymore
234 if (debug) printf("Did a partial fill to help quench thirst for data\n");
235 } else if (length < control->gtls_buffer_hack_len) { // If it's asking for less...
236 control->gtls_buffer_hack_len -= length; // subtract what they're asking for
237 memcpy(buffer, control->gtls_buffer_hack, length); // fill their buffer
238 hackhackhack = (char*)malloc(sizeof(char) * control->gtls_buffer_hack_len); // strndup is NOT a good solution -- concatenates \0!!!! Anyway, make a new "hack" buffer.
239 memcpy(hackhackhack, control->gtls_buffer_hack+length, control->gtls_buffer_hack_len); // Move what's left into the new one
240 free(control->gtls_buffer_hack); // Free the old one
241 control->gtls_buffer_hack = hackhackhack; // And make it the new one.
242 hackhackhack = NULL;
243 if (debug) printf("Quenched the thirst for data; new hack length is %i\n", control->gtls_buffer_hack_len);
244 return length; // hand it over.
245 } else { // length == hack length
246 memcpy(buffer, control->gtls_buffer_hack, length); // copy our buffer into theirs
247 free(control->gtls_buffer_hack); // free our "obligation"
248 control->gtls_buffer_hack_len = 0; // free our "obligation"
249 if (debug) printf("Satiated the thirst for data; now we have to eventually receive again.\n");
250 return length; // hand it over
251 }
252 }
253 // End buffering hack!
254 char *recv_buffer = (char*)malloc(sizeof(char) * (length * 400)); // ensuring nothing stupid happens
255
256 if (debug) printf("pre-read\nclient wants %i bytes\n", length);
257 bytes = mux_recv(control->iphone, control->connection, recv_buffer, (length * 400));
258 if (debug) printf("post-read\nwe got %i bytes\n", bytes);
259 if (bytes >= length) {
260 if (bytes > length) {
261 if (debug) printf("lockdownd_securead: Client deliberately read less data than was there; resorting to GnuTLS buffering hack.\n");
262 if (!control->gtls_buffer_hack_len) { // if there's no hack buffer yet
263 //control->gtls_buffer_hack = strndup(recv_buffer+length, bytes-length); // strndup is NOT a good solution!
264 control->gtls_buffer_hack_len += bytes-length;
265 control->gtls_buffer_hack = (char*)malloc(sizeof(char) * control->gtls_buffer_hack_len);
266 memcpy(control->gtls_buffer_hack, recv_buffer+length, control->gtls_buffer_hack_len);
267 } else { // if there is.
268 control->gtls_buffer_hack = realloc(control->gtls_buffer_hack, control->gtls_buffer_hack_len + (bytes - length));
269 memcpy(control->gtls_buffer_hack+control->gtls_buffer_hack_len, recv_buffer+length, bytes-length);
270 control->gtls_buffer_hack_len += bytes - length;
271 }
272 }
273 memcpy(buffer+pos_start_fill, recv_buffer, length);
274 free(recv_buffer);
275 if (bytes == length) { if (debug) printf("Returning how much we received.\n"); return bytes; }
276 else { if (debug) printf("Returning what they want to hear.\nHack length: %i\n", control->gtls_buffer_hack_len); return length; }
277 }
278 return bytes;
279}
280
281int lockdownd_start_service(lockdownd_client *control, const char *service) {
282 if (!control) return 0;
283 if (!control->in_SSL && !lockdownd_start_SSL_session(control, "29942970-207913891623273984")) return 0;
284
285 char *XML_query, **dictionary;
286 uint32 length, i = 0, port = 0;
287 uint8 result = 0;
288
289 xmlDocPtr plist = new_plist();
290 xmlNode *dict = add_child_to_plist(plist, "dict", "\n", NULL, 0);
291 xmlNode *key;
292 key = add_key_str_dict_element(plist, dict, "Request", "StartService", 1);
293 if (!key) { xmlFreeDoc(plist); return 0; }
294 key = add_key_str_dict_element(plist, dict, "Service", service, 1);
295 if (!key) { xmlFreeDoc(plist); return 0; }
296
297 xmlDocDumpMemory(plist, &XML_query, &length);
298
299 lockdownd_send(control, XML_query, length);
300 free(XML_query);
301
302 length = lockdownd_recv(control, &XML_query);
303
304 xmlFreeDoc(plist);
305
306 if (length <= 0) return 0;
307 else {
308 plist = xmlReadMemory(XML_query, length, NULL, NULL, 0);
309 if (!plist) return 0;
310 dict = xmlDocGetRootElement(plist);
311 if (!dict) return 0;
312 for (dict = dict->children; dict; dict = dict->next) {
313 if (!xmlStrcmp(dict->name, "dict")) break;
314 }
315
316 if (!dict) return 0;
317 dictionary = read_dict_element_strings(dict);
318
319 for (i = 0; strcmp(dictionary[i], ""); i+=2) {
320 if (debug) printf("lockdownd_start_service() dictionary %s: %s\n", dictionary[i], dictionary[i+1]);
321
322 if (!xmlStrcmp(dictionary[i], "Port")) {
323 port = atoi(dictionary[i+1]);
324 if (debug) printf("lockdownd_start_service() atoi'd port: %i\n", port);
325 }
326
327 if (!xmlStrcmp(dictionary[i], "Result")) {
328 if (!xmlStrcmp(dictionary[i+1], "Success")) {
329 result = 1;
330 }
331 }
332 }
333
334 if (debug) {
335 printf("lockdownd_start_service(): DATA RECEIVED:\n\n");
336 fwrite(XML_query, 1, length, stdout);
337 printf("end data received by lockdownd_start_service()\n");
338 }
339
340 free(XML_query);
341 xmlFreeDoc(plist);
342 free_dictionary(dictionary);
343 if (port && result) return port;
344 else return 0;
345 }
346
347 return 0;
348}
349