diff options
| author | 2008-12-13 12:21:03 +0100 | |
|---|---|---|
| committer | 2008-12-13 12:21:03 +0100 | |
| commit | 3fdd24aea06a9bf38d9d34fb8bccbb7023ed3100 (patch) | |
| tree | 1080d26eca01c885efb33f3f98821a981a25e8b4 /src | |
| parent | 3d8ba053deeacd74e621469d3d45d1db38ee411a (diff) | |
| download | libplist-3fdd24aea06a9bf38d9d34fb8bccbb7023ed3100.tar.gz libplist-3fdd24aea06a9bf38d9d34fb8bccbb7023ed3100.tar.bz2 | |
Fork libiphone and remove anything non plist specific.
Update library and make related files acordingly .
Diffstat (limited to 'src')
| -rw-r--r-- | src/AFC.c | 1018 | ||||
| -rw-r--r-- | src/AFC.h | 77 | ||||
| -rw-r--r-- | src/Makefile.am | 16 | ||||
| -rw-r--r-- | src/initconf.c | 213 | ||||
| -rw-r--r-- | src/iphone.c | 247 | ||||
| -rw-r--r-- | src/iphone.h | 45 | ||||
| -rw-r--r-- | src/lockdown.c | 969 | ||||
| -rw-r--r-- | src/lockdown.h | 62 | ||||
| -rw-r--r-- | src/usbmux.c | 381 | ||||
| -rw-r--r-- | src/usbmux.h | 58 | ||||
| -rw-r--r-- | src/userpref.c | 285 | ||||
| -rw-r--r-- | src/userpref.h | 71 |
12 files changed, 4 insertions, 3438 deletions
diff --git a/src/AFC.c b/src/AFC.c deleted file mode 100644 index 899bd47..0000000 --- a/src/AFC.c +++ /dev/null | |||
| @@ -1,1018 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * AFC.c | ||
| 3 | * Contains functions for the built-in AFC client. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <stdio.h> | ||
| 23 | #include "AFC.h" | ||
| 24 | #include "plist.h" | ||
| 25 | |||
| 26 | |||
| 27 | // This is the maximum size an AFC data packet can be | ||
| 28 | const int MAXIMUM_PACKET_SIZE = (2 << 15) - 32; | ||
| 29 | |||
| 30 | /** Locks an AFC client, done for thread safety stuff | ||
| 31 | * | ||
| 32 | * @param client The AFC client connection to lock | ||
| 33 | */ | ||
| 34 | static void afc_lock(iphone_afc_client_t client) | ||
| 35 | { | ||
| 36 | log_debug_msg("Locked\n"); | ||
| 37 | /*while (client->lock) { | ||
| 38 | usleep(500); // they say it's obsolete, but whatever | ||
| 39 | } | ||
| 40 | client->lock = 1; */ | ||
| 41 | g_mutex_lock(client->mutex); | ||
| 42 | } | ||
| 43 | |||
| 44 | /** Unlocks an AFC client, done for thread safety stuff. | ||
| 45 | * | ||
| 46 | * @param client The AFC | ||
| 47 | */ | ||
| 48 | static void afc_unlock(iphone_afc_client_t client) | ||
| 49 | { // just to be pretty | ||
| 50 | log_debug_msg("Unlocked\n"); | ||
| 51 | //client->lock = 0; | ||
| 52 | g_mutex_unlock(client->mutex); | ||
| 53 | } | ||
| 54 | |||
| 55 | /** Makes a connection to the AFC service on the phone. | ||
| 56 | * | ||
| 57 | * @param phone The iPhone to connect on. | ||
| 58 | * @param s_port The source port. | ||
| 59 | * @param d_port The destination port. | ||
| 60 | * | ||
| 61 | * @return A handle to the newly-connected client or NULL upon error. | ||
| 62 | */ | ||
| 63 | iphone_error_t iphone_afc_new_client(iphone_device_t device, int src_port, int dst_port, iphone_afc_client_t * client) | ||
| 64 | { | ||
| 65 | int ret = IPHONE_E_SUCCESS; | ||
| 66 | |||
| 67 | //makes sure thread environment is available | ||
| 68 | if (!g_thread_supported()) | ||
| 69 | g_thread_init(NULL); | ||
| 70 | iphone_afc_client_t client_loc = (iphone_afc_client_t) malloc(sizeof(struct iphone_afc_client_int)); | ||
| 71 | |||
| 72 | if (!device) | ||
| 73 | return IPHONE_E_INVALID_ARG; | ||
| 74 | |||
| 75 | // Attempt connection | ||
| 76 | client_loc->connection = NULL; | ||
| 77 | ret = iphone_mux_new_client(device, src_port, dst_port, &client_loc->connection); | ||
| 78 | if (IPHONE_E_SUCCESS != ret || !client_loc->connection) { | ||
| 79 | free(client_loc); | ||
| 80 | return ret; | ||
| 81 | } | ||
| 82 | // Allocate a packet | ||
| 83 | client_loc->afc_packet = (AFCPacket *) malloc(sizeof(AFCPacket)); | ||
| 84 | if (!client_loc->afc_packet) { | ||
| 85 | iphone_mux_free_client(client_loc->connection); | ||
| 86 | free(client_loc); | ||
| 87 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 88 | } | ||
| 89 | |||
| 90 | client_loc->afc_packet->packet_num = 0; | ||
| 91 | client_loc->afc_packet->unknown1 = 0; | ||
| 92 | client_loc->afc_packet->unknown2 = 0; | ||
| 93 | client_loc->afc_packet->unknown3 = 0; | ||
| 94 | client_loc->afc_packet->unknown4 = 0; | ||
| 95 | client_loc->afc_packet->entire_length = 0; | ||
| 96 | client_loc->afc_packet->this_length = 0; | ||
| 97 | client_loc->afc_packet->header1 = 0x36414643; | ||
| 98 | client_loc->afc_packet->header2 = 0x4141504C; | ||
| 99 | client_loc->file_handle = 0; | ||
| 100 | client_loc->lock = 0; | ||
| 101 | client_loc->mutex = g_mutex_new(); | ||
| 102 | |||
| 103 | *client = client_loc; | ||
| 104 | return IPHONE_E_SUCCESS; | ||
| 105 | } | ||
| 106 | |||
| 107 | /** Disconnects an AFC client from the phone. | ||
| 108 | * | ||
| 109 | * @param client The client to disconnect. | ||
| 110 | */ | ||
| 111 | iphone_error_t iphone_afc_free_client(iphone_afc_client_t client) | ||
| 112 | { | ||
| 113 | if (!client || !client->connection || !client->afc_packet) | ||
| 114 | return IPHONE_E_INVALID_ARG; | ||
| 115 | |||
| 116 | iphone_mux_free_client(client->connection); | ||
| 117 | free(client->afc_packet); | ||
| 118 | free(client); | ||
| 119 | return IPHONE_E_SUCCESS; | ||
| 120 | } | ||
| 121 | |||
| 122 | |||
| 123 | /** Dispatches an AFC packet over a client. | ||
| 124 | * | ||
| 125 | * @param client The client to send data through. | ||
| 126 | * @param data The data to send. | ||
| 127 | * @param length The length to send. | ||
| 128 | * | ||
| 129 | * @return The number of bytes actually sent, or -1 on error. | ||
| 130 | * | ||
| 131 | * @warning set client->afc_packet->this_length and | ||
| 132 | * client->afc_packet->entire_length to 0 before calling this. The | ||
| 133 | * reason is that if you set them to different values, it indicates | ||
| 134 | * you want to send the data as two packets. | ||
| 135 | */ | ||
| 136 | static int dispatch_AFC_packet(iphone_afc_client_t client, const char *data, int length) | ||
| 137 | { | ||
| 138 | int bytes = 0, offset = 0; | ||
| 139 | char *buffer; | ||
| 140 | |||
| 141 | if (!client || !client->connection || !client->afc_packet) | ||
| 142 | return 0; | ||
| 143 | if (!data || !length) | ||
| 144 | length = 0; | ||
| 145 | |||
| 146 | client->afc_packet->packet_num++; | ||
| 147 | if (!client->afc_packet->entire_length) { | ||
| 148 | client->afc_packet->entire_length = (length) ? sizeof(AFCPacket) + length + 1 : sizeof(AFCPacket); | ||
| 149 | client->afc_packet->this_length = client->afc_packet->entire_length; | ||
| 150 | } | ||
| 151 | if (!client->afc_packet->this_length) { | ||
| 152 | client->afc_packet->this_length = sizeof(AFCPacket); | ||
| 153 | } | ||
| 154 | // We want to send two segments; buffer+sizeof(AFCPacket) to | ||
| 155 | // this_length is the parameters | ||
| 156 | // And everything beyond that is the next packet. (for writing) | ||
| 157 | if (client->afc_packet->this_length != client->afc_packet->entire_length) { | ||
| 158 | buffer = (char *) malloc(client->afc_packet->this_length); | ||
| 159 | memcpy(buffer, (char *) client->afc_packet, sizeof(AFCPacket)); | ||
| 160 | offset = client->afc_packet->this_length - sizeof(AFCPacket); | ||
| 161 | |||
| 162 | log_debug_msg("dispatch_AFC_packet: Offset: %i\n", offset); | ||
| 163 | if ((length) < (client->afc_packet->entire_length - client->afc_packet->this_length)) { | ||
| 164 | log_debug_msg("dispatch_AFC_packet: Length did not resemble what it was supposed"); | ||
| 165 | log_debug_msg("to based on the packet.\n"); | ||
| 166 | log_debug_msg("length minus offset: %i\n", length - offset); | ||
| 167 | log_debug_msg("rest of packet: %i\n", client->afc_packet->entire_length - client->afc_packet->this_length); | ||
| 168 | free(buffer); | ||
| 169 | return -1; | ||
| 170 | } | ||
| 171 | memcpy(buffer + sizeof(AFCPacket), data, offset); | ||
| 172 | iphone_mux_send(client->connection, buffer, client->afc_packet->this_length, &bytes); | ||
| 173 | free(buffer); | ||
| 174 | if (bytes <= 0) { | ||
| 175 | return bytes; | ||
| 176 | } | ||
| 177 | |||
| 178 | log_debug_msg("dispatch_AFC_packet: sent the first now go with the second\n"); | ||
| 179 | log_debug_msg("Length: %i\n", length - offset); | ||
| 180 | log_debug_msg("Buffer: \n"); | ||
| 181 | log_debug_msg(data + offset); | ||
| 182 | |||
| 183 | iphone_mux_send(client->connection, data + offset, length - offset, &bytes); | ||
| 184 | return bytes; | ||
| 185 | } else { | ||
| 186 | log_debug_msg("dispatch_AFC_packet doin things the old way\n"); | ||
| 187 | char *buffer = (char *) malloc(sizeof(char) * client->afc_packet->this_length); | ||
| 188 | log_debug_msg("dispatch_AFC_packet packet length = %i\n", client->afc_packet->this_length); | ||
| 189 | memcpy(buffer, (char *) client->afc_packet, sizeof(AFCPacket)); | ||
| 190 | log_debug_msg("dispatch_AFC_packet packet data follows\n"); | ||
| 191 | if (length > 0) { | ||
| 192 | memcpy(buffer + sizeof(AFCPacket), data, length); | ||
| 193 | buffer[sizeof(AFCPacket) + length] = '\0'; | ||
| 194 | } | ||
| 195 | log_debug_buffer(buffer, client->afc_packet->this_length); | ||
| 196 | log_debug_msg("\n"); | ||
| 197 | iphone_mux_send(client->connection, buffer, client->afc_packet->this_length, &bytes); | ||
| 198 | |||
| 199 | if (buffer) { | ||
| 200 | free(buffer); | ||
| 201 | buffer = NULL; | ||
| 202 | } | ||
| 203 | return bytes; | ||
| 204 | } | ||
| 205 | return -1; | ||
| 206 | } | ||
| 207 | |||
| 208 | /** Receives data through an AFC client and sets a variable to the received data. | ||
| 209 | * | ||
| 210 | * @param client The client to receive data on. | ||
| 211 | * @param dump_here The char* to point to the newly-received data. | ||
| 212 | * | ||
| 213 | * @return How much data was received, 0 on successful receive with no errors, | ||
| 214 | * -1 if there was an error involved with receiving or if the packet | ||
| 215 | * received raised a non-trivial error condition (i.e. non-zero with | ||
| 216 | * AFC_ERROR operation) | ||
| 217 | */ | ||
| 218 | |||
| 219 | static int receive_AFC_data(iphone_afc_client_t client, char **dump_here) | ||
| 220 | { | ||
| 221 | AFCPacket *r_packet; | ||
| 222 | char *buffer = (char *) malloc(sizeof(AFCPacket) * 4); | ||
| 223 | char *final_buffer = NULL; | ||
| 224 | int bytes = 0, recv_len = 0, current_count = 0; | ||
| 225 | int retval = 0; | ||
| 226 | |||
| 227 | iphone_mux_recv(client->connection, buffer, sizeof(AFCPacket) * 4, &bytes); | ||
| 228 | if (bytes <= 0) { | ||
| 229 | free(buffer); | ||
| 230 | fprintf(stderr, "Just didn't get enough.\n"); | ||
| 231 | *dump_here = NULL; | ||
| 232 | return -1; | ||
| 233 | } | ||
| 234 | |||
| 235 | r_packet = (AFCPacket *) malloc(sizeof(AFCPacket)); | ||
| 236 | memcpy(r_packet, buffer, sizeof(AFCPacket)); | ||
| 237 | |||
| 238 | if (r_packet->entire_length == r_packet->this_length | ||
| 239 | && r_packet->entire_length > sizeof(AFCPacket) && r_packet->operation != AFC_ERROR) { | ||
| 240 | *dump_here = (char *) malloc(sizeof(char) * (r_packet->entire_length - sizeof(AFCPacket))); | ||
| 241 | memcpy(*dump_here, buffer + sizeof(AFCPacket), r_packet->entire_length - sizeof(AFCPacket)); | ||
| 242 | retval = r_packet->entire_length - sizeof(AFCPacket); | ||
| 243 | free(buffer); | ||
| 244 | free(r_packet); | ||
| 245 | return retval; | ||
| 246 | } | ||
| 247 | |||
| 248 | uint32_t param1 = buffer[sizeof(AFCPacket)]; | ||
| 249 | free(buffer); | ||
| 250 | |||
| 251 | if (r_packet->operation == AFC_ERROR && !(client->afc_packet->operation == AFC_DELETE && param1 == 7)) { | ||
| 252 | log_debug_msg("Oops? Bad operation code received: 0x%X, operation=0x%X, param1=%d\n", | ||
| 253 | r_packet->operation, client->afc_packet->operation, param1); | ||
| 254 | recv_len = r_packet->entire_length - r_packet->this_length; | ||
| 255 | free(r_packet); | ||
| 256 | log_debug_msg("recv_len=%d\n", recv_len); | ||
| 257 | if (param1 == 0) { | ||
| 258 | log_debug_msg("... false alarm, but still\n"); | ||
| 259 | *dump_here = NULL; | ||
| 260 | return 0; | ||
| 261 | } else { | ||
| 262 | log_debug_msg("Errno %i\n", param1); | ||
| 263 | } | ||
| 264 | *dump_here = NULL; | ||
| 265 | return -1; | ||
| 266 | } else { | ||
| 267 | log_debug_msg("Operation code %x\nFull length %i and this length %i\n", | ||
| 268 | r_packet->operation, r_packet->entire_length, r_packet->this_length); | ||
| 269 | } | ||
| 270 | |||
| 271 | recv_len = r_packet->entire_length - r_packet->this_length; | ||
| 272 | free(r_packet); | ||
| 273 | if (!recv_len && r_packet->operation == AFC_SUCCESS_RESPONSE) { | ||
| 274 | *dump_here = NULL; | ||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | // Keep collecting packets until we have received the entire file. | ||
| 278 | buffer = (char *) malloc(sizeof(char) * (recv_len < MAXIMUM_PACKET_SIZE) ? recv_len : MAXIMUM_PACKET_SIZE); | ||
| 279 | final_buffer = (char *) malloc(sizeof(char) * recv_len); | ||
| 280 | while (current_count < recv_len) { | ||
| 281 | iphone_mux_recv(client->connection, buffer, recv_len - current_count, &bytes); | ||
| 282 | log_debug_msg("receive_AFC_data: still collecting packets\n"); | ||
| 283 | if (bytes < 0) { | ||
| 284 | log_debug_msg("receive_AFC_data: mux_recv failed: %d\n", bytes); | ||
| 285 | break; | ||
| 286 | } | ||
| 287 | if (bytes > recv_len - current_count) { | ||
| 288 | log_debug_msg("receive_AFC_data: mux_recv delivered too much data\n"); | ||
| 289 | break; | ||
| 290 | } | ||
| 291 | if (bytes > 7 && strstr(buffer, "CFA6LPAA")) { | ||
| 292 | log_debug_msg("receive_AFC_data: WARNING: there is AFC data in this packet at %ti\n", | ||
| 293 | strstr(buffer, "CFA6LPAA") - buffer); | ||
| 294 | log_debug_msg("receive_AFC_data: the total packet length is %i\n", bytes); | ||
| 295 | } | ||
| 296 | |||
| 297 | memcpy(final_buffer + current_count, buffer, bytes); | ||
| 298 | current_count += bytes; | ||
| 299 | } | ||
| 300 | free(buffer); | ||
| 301 | |||
| 302 | *dump_here = final_buffer; | ||
| 303 | return current_count; | ||
| 304 | } | ||
| 305 | |||
| 306 | static int count_nullspaces(char *string, int number) | ||
| 307 | { | ||
| 308 | int i = 0, nulls = 0; | ||
| 309 | |||
| 310 | for (i = 0; i < number; i++) { | ||
| 311 | if (string[i] == '\0') | ||
| 312 | nulls++; | ||
| 313 | } | ||
| 314 | |||
| 315 | return nulls; | ||
| 316 | } | ||
| 317 | |||
| 318 | static char **make_strings_list(char *tokens, int true_length) | ||
| 319 | { | ||
| 320 | int nulls = 0, i = 0, j = 0; | ||
| 321 | char **list = NULL; | ||
| 322 | |||
| 323 | if (!tokens || !true_length) | ||
| 324 | return NULL; | ||
| 325 | |||
| 326 | nulls = count_nullspaces(tokens, true_length); | ||
| 327 | list = (char **) malloc(sizeof(char *) * (nulls + 1)); | ||
| 328 | for (i = 0; i < nulls; i++) { | ||
| 329 | list[i] = strdup(tokens + j); | ||
| 330 | j += strlen(list[i]) + 1; | ||
| 331 | } | ||
| 332 | list[i] = NULL; | ||
| 333 | |||
| 334 | return list; | ||
| 335 | } | ||
| 336 | |||
| 337 | /** Gets a directory listing of the directory requested. | ||
| 338 | * | ||
| 339 | * @param client The client to get a directory listing from. | ||
| 340 | * @param dir The directory to list. (must be a fully-qualified path) | ||
| 341 | * | ||
| 342 | * @return A char ** list of files in that directory, terminated by an empty | ||
| 343 | * string for now or NULL if there was an error. | ||
| 344 | */ | ||
| 345 | iphone_error_t iphone_afc_get_dir_list(iphone_afc_client_t client, const char *dir, char ***list) | ||
| 346 | { | ||
| 347 | int bytes = 0; | ||
| 348 | char *data = NULL, **list_loc = NULL; | ||
| 349 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 350 | |||
| 351 | if (!client || !dir || !list || (list && *list)) | ||
| 352 | return IPHONE_E_INVALID_ARG; | ||
| 353 | |||
| 354 | afc_lock(client); | ||
| 355 | |||
| 356 | // Send the command | ||
| 357 | client->afc_packet->operation = AFC_LIST_DIR; | ||
| 358 | client->afc_packet->entire_length = 0; | ||
| 359 | client->afc_packet->this_length = 0; | ||
| 360 | bytes = dispatch_AFC_packet(client, dir, strlen(dir)); | ||
| 361 | if (bytes <= 0) { | ||
| 362 | afc_unlock(client); | ||
| 363 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 364 | } | ||
| 365 | // Receive the data | ||
| 366 | bytes = receive_AFC_data(client, &data); | ||
| 367 | if (bytes < 0 && !data) { | ||
| 368 | afc_unlock(client); | ||
| 369 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 370 | } | ||
| 371 | // Parse the data | ||
| 372 | list_loc = make_strings_list(data, bytes); | ||
| 373 | if (list_loc) | ||
| 374 | ret = IPHONE_E_SUCCESS; | ||
| 375 | if (data) | ||
| 376 | free(data); | ||
| 377 | |||
| 378 | afc_unlock(client); | ||
| 379 | *list = list_loc; | ||
| 380 | |||
| 381 | return ret; | ||
| 382 | } | ||
| 383 | |||
| 384 | /** Get device info for a client connection to phone. (free space on disk, etc.) | ||
| 385 | * | ||
| 386 | * @param client The client to get device info for. | ||
| 387 | * | ||
| 388 | * @return A char ** list of parameters as given by AFC or NULL if there was an | ||
| 389 | * error. | ||
| 390 | */ | ||
| 391 | iphone_error_t iphone_afc_get_devinfo(iphone_afc_client_t client, char ***infos) | ||
| 392 | { | ||
| 393 | int bytes = 0; | ||
| 394 | char *data = NULL, **list = NULL; | ||
| 395 | |||
| 396 | if (!client || !infos) | ||
| 397 | return IPHONE_E_INVALID_ARG; | ||
| 398 | |||
| 399 | afc_lock(client); | ||
| 400 | |||
| 401 | // Send the command | ||
| 402 | client->afc_packet->operation = AFC_GET_DEVINFO; | ||
| 403 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
| 404 | bytes = dispatch_AFC_packet(client, NULL, 0); | ||
| 405 | if (bytes < 0) { | ||
| 406 | afc_unlock(client); | ||
| 407 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 408 | } | ||
| 409 | // Receive the data | ||
| 410 | bytes = receive_AFC_data(client, &data); | ||
| 411 | if (bytes < 0 && !data) { | ||
| 412 | afc_unlock(client); | ||
| 413 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 414 | } | ||
| 415 | // Parse the data | ||
| 416 | list = make_strings_list(data, bytes); | ||
| 417 | if (data) | ||
| 418 | free(data); | ||
| 419 | |||
| 420 | afc_unlock(client); | ||
| 421 | *infos = list; | ||
| 422 | return IPHONE_E_SUCCESS; | ||
| 423 | } | ||
| 424 | |||
| 425 | /** Deletes a file. | ||
| 426 | * | ||
| 427 | * @param client The client to have delete the file. | ||
| 428 | * @param path The file to delete. (must be a fully-qualified path) | ||
| 429 | * | ||
| 430 | * @return IPHONE_E_SUCCESS if everythong went well, IPHONE_E_INVALID_ARG | ||
| 431 | * if arguments are NULL or invalid, IPHONE_E_NOT_ENOUGH_DATA otherwise. | ||
| 432 | */ | ||
| 433 | iphone_error_t iphone_afc_delete_file(iphone_afc_client_t client, const char *path) | ||
| 434 | { | ||
| 435 | char *response = NULL; | ||
| 436 | int bytes; | ||
| 437 | |||
| 438 | if (!client || !path || !client->afc_packet || !client->connection) | ||
| 439 | return IPHONE_E_INVALID_ARG; | ||
| 440 | |||
| 441 | afc_lock(client); | ||
| 442 | |||
| 443 | // Send command | ||
| 444 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | ||
| 445 | client->afc_packet->operation = AFC_DELETE; | ||
| 446 | bytes = dispatch_AFC_packet(client, path, strlen(path)); | ||
| 447 | if (bytes <= 0) { | ||
| 448 | afc_unlock(client); | ||
| 449 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 450 | } | ||
| 451 | // Receive response | ||
| 452 | bytes = receive_AFC_data(client, &response); | ||
| 453 | if (response) | ||
| 454 | free(response); | ||
| 455 | |||
| 456 | afc_unlock(client); | ||
| 457 | |||
| 458 | if (bytes < 0) { | ||
| 459 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 460 | } else { | ||
| 461 | return IPHONE_E_SUCCESS; | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | /** Renames a file on the phone. | ||
| 466 | * | ||
| 467 | * @param client The client to have rename the file. | ||
| 468 | * @param from The file to rename. (must be a fully-qualified path) | ||
| 469 | * @param to The new name of the file. (must also be a fully-qualified path) | ||
| 470 | * | ||
| 471 | * @return IPHONE_E_SUCCESS if everythong went well, IPHONE_E_INVALID_ARG | ||
| 472 | * if arguments are NULL or invalid, IPHONE_E_NOT_ENOUGH_DATA otherwise. | ||
| 473 | */ | ||
| 474 | iphone_error_t iphone_afc_rename_file(iphone_afc_client_t client, const char *from, const char *to) | ||
| 475 | { | ||
| 476 | char *response = NULL; | ||
| 477 | char *send = (char *) malloc(sizeof(char) * (strlen(from) + strlen(to) + 1 + sizeof(uint32_t))); | ||
| 478 | int bytes = 0; | ||
| 479 | |||
| 480 | if (!client || !from || !to || !client->afc_packet || !client->connection) | ||
| 481 | return IPHONE_E_INVALID_ARG; | ||
| 482 | |||
| 483 | afc_lock(client); | ||
| 484 | |||
| 485 | // Send command | ||
| 486 | memcpy(send, from, strlen(from) + 1); | ||
| 487 | memcpy(send + strlen(from) + 1, to, strlen(to) + 1); | ||
| 488 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
| 489 | client->afc_packet->operation = AFC_RENAME; | ||
| 490 | bytes = dispatch_AFC_packet(client, send, strlen(to) + strlen(from) + 2); | ||
| 491 | free(send); | ||
| 492 | if (bytes <= 0) { | ||
| 493 | afc_unlock(client); | ||
| 494 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 495 | } | ||
| 496 | // Receive response | ||
| 497 | bytes = receive_AFC_data(client, &response); | ||
| 498 | if (response) | ||
| 499 | free(response); | ||
| 500 | |||
| 501 | afc_unlock(client); | ||
| 502 | |||
| 503 | if (bytes < 0) { | ||
| 504 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 505 | } else { | ||
| 506 | return IPHONE_E_SUCCESS; | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | /** Creates a directory on the phone. | ||
| 511 | * | ||
| 512 | * @param client The client to use to make a directory. | ||
| 513 | * @param dir The directory's path. (must be a fully-qualified path, I assume | ||
| 514 | * all other mkdir restrictions apply as well) | ||
| 515 | * | ||
| 516 | * @return IPHONE_E_SUCCESS if everythong went well, IPHONE_E_INVALID_ARG | ||
| 517 | * if arguments are NULL or invalid, IPHONE_E_NOT_ENOUGH_DATA otherwise. | ||
| 518 | */ | ||
| 519 | iphone_error_t iphone_afc_mkdir(iphone_afc_client_t client, const char *dir) | ||
| 520 | { | ||
| 521 | int bytes = 0; | ||
| 522 | char *response = NULL; | ||
| 523 | |||
| 524 | if (!client) | ||
| 525 | return IPHONE_E_INVALID_ARG; | ||
| 526 | |||
| 527 | afc_lock(client); | ||
| 528 | |||
| 529 | // Send command | ||
| 530 | client->afc_packet->operation = AFC_MAKE_DIR; | ||
| 531 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | ||
| 532 | bytes = dispatch_AFC_packet(client, dir, strlen(dir)); | ||
| 533 | if (bytes <= 0) { | ||
| 534 | afc_unlock(client); | ||
| 535 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 536 | } | ||
| 537 | // Receive response | ||
| 538 | bytes = receive_AFC_data(client, &response); | ||
| 539 | if (response) | ||
| 540 | free(response); | ||
| 541 | |||
| 542 | afc_unlock(client); | ||
| 543 | |||
| 544 | if (bytes < 0) { | ||
| 545 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 546 | } else { | ||
| 547 | return IPHONE_E_SUCCESS; | ||
| 548 | } | ||
| 549 | } | ||
| 550 | |||
| 551 | /** Gets information about a specific file. | ||
| 552 | * | ||
| 553 | * @param client The client to use to get the information of the file. | ||
| 554 | * @param path The fully-qualified path to the file. | ||
| 555 | * | ||
| 556 | * @return A pointer to an AFCFile struct containing the information received, | ||
| 557 | * or NULL on failure. | ||
| 558 | */ | ||
| 559 | iphone_afc_file_t afc_get_file_info(iphone_afc_client_t client, const char *path) | ||
| 560 | { | ||
| 561 | char *received, **list; | ||
| 562 | iphone_afc_file_t my_file; | ||
| 563 | int length, i = 0; | ||
| 564 | |||
| 565 | afc_lock(client); | ||
| 566 | |||
| 567 | // Send command | ||
| 568 | client->afc_packet->operation = AFC_GET_INFO; | ||
| 569 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
| 570 | dispatch_AFC_packet(client, path, strlen(path)); | ||
| 571 | |||
| 572 | // Receive data | ||
| 573 | length = receive_AFC_data(client, &received); | ||
| 574 | if (received) { | ||
| 575 | list = make_strings_list(received, length); | ||
| 576 | free(received); | ||
| 577 | } else { | ||
| 578 | afc_unlock(client); | ||
| 579 | return NULL; | ||
| 580 | } | ||
| 581 | |||
| 582 | afc_unlock(client); | ||
| 583 | |||
| 584 | // Parse the data | ||
| 585 | if (list) { | ||
| 586 | my_file = (iphone_afc_file_t) malloc(sizeof(struct iphone_afc_file_int)); | ||
| 587 | for (i = 0; list[i]; i++) { | ||
| 588 | if (!strcmp(list[i], "st_size")) { | ||
| 589 | my_file->size = atoi(list[i + 1]); | ||
| 590 | } | ||
| 591 | |||
| 592 | if (!strcmp(list[i], "st_blocks")) { | ||
| 593 | my_file->blocks = atoi(list[i + 1]); | ||
| 594 | } | ||
| 595 | |||
| 596 | if (!strcmp(list[i], "st_ifmt")) { | ||
| 597 | if (!strcmp(list[i + 1], "S_IFREG")) { | ||
| 598 | my_file->type = S_IFREG; | ||
| 599 | } else if (!strcmp(list[i + 1], "S_IFDIR")) { | ||
| 600 | my_file->type = S_IFDIR; | ||
| 601 | } | ||
| 602 | } | ||
| 603 | } | ||
| 604 | g_strfreev(list); | ||
| 605 | return my_file; | ||
| 606 | } else { | ||
| 607 | return NULL; | ||
| 608 | } | ||
| 609 | } | ||
| 610 | |||
| 611 | /** Gets information about a specific file. | ||
| 612 | * | ||
| 613 | * @param client The client to use to get the information of the file. | ||
| 614 | * @param path The fully-qualified path to the file | ||
| 615 | * @param stbuf output buffer where file information will be stored | ||
| 616 | * | ||
| 617 | * @return A pointer to an AFCFile struct containing the information received, | ||
| 618 | * or NULL on failure. | ||
| 619 | */ | ||
| 620 | iphone_error_t iphone_afc_get_file_attr(iphone_afc_client_t client, const char *filename, struct stat * stbuf) | ||
| 621 | { | ||
| 622 | |||
| 623 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 624 | if (!client || !client->connection || !client->afc_packet || !stbuf) | ||
| 625 | return IPHONE_E_INVALID_ARG; | ||
| 626 | |||
| 627 | memset(stbuf, 0, sizeof(struct stat)); | ||
| 628 | iphone_afc_file_t file = afc_get_file_info(client, filename); | ||
| 629 | if (!file) { | ||
| 630 | ret = IPHONE_E_NO_SUCH_FILE; | ||
| 631 | } else { | ||
| 632 | stbuf->st_mode = file->type | (S_ISDIR(file->type) ? 0755 : 0644); | ||
| 633 | stbuf->st_size = file->size; | ||
| 634 | stbuf->st_blksize = 2048; // FIXME: Is this the actual block | ||
| 635 | // size used on the iPhone? | ||
| 636 | stbuf->st_blocks = file->blocks; | ||
| 637 | stbuf->st_uid = getuid(); | ||
| 638 | stbuf->st_gid = getgid(); | ||
| 639 | |||
| 640 | ret = iphone_afc_close_file(client, file); | ||
| 641 | } | ||
| 642 | return ret; | ||
| 643 | } | ||
| 644 | |||
| 645 | /** Opens a file on the phone. | ||
| 646 | * | ||
| 647 | * @param client The client to use to open the file. | ||
| 648 | * @param filename The file to open. (must be a fully-qualified path) | ||
| 649 | * @param file_mode The mode to use to open the file. Can be AFC_FILE_READ or | ||
| 650 | * AFC_FILE_WRITE; the former lets you read and write, | ||
| 651 | * however, and the second one will *create* the file, | ||
| 652 | * destroying anything previously there. | ||
| 653 | * | ||
| 654 | * @return A pointer to an AFCFile struct containing the file information (as | ||
| 655 | * received by afc_get_file_info) as well as the handle to the file or | ||
| 656 | * NULL in the case of failure. | ||
| 657 | */ | ||
| 658 | iphone_error_t | ||
| 659 | iphone_afc_open_file(iphone_afc_client_t client, const char *filename, | ||
| 660 | iphone_afc_file_mode_t file_mode, iphone_afc_file_t * file) | ||
| 661 | { | ||
| 662 | iphone_afc_file_t file_loc = NULL; | ||
| 663 | uint32_t ag = 0; | ||
| 664 | int bytes = 0, length = 0; | ||
| 665 | char *data = (char *) malloc(sizeof(char) * (8 + strlen(filename) + 1)); | ||
| 666 | |||
| 667 | if (!client || !client->connection || !client->afc_packet) | ||
| 668 | return IPHONE_E_INVALID_ARG; | ||
| 669 | |||
| 670 | afc_lock(client); | ||
| 671 | |||
| 672 | // Send command | ||
| 673 | memcpy(data, &file_mode, 4); | ||
| 674 | memcpy(data + 4, &ag, 4); | ||
| 675 | memcpy(data + 8, filename, strlen(filename)); | ||
| 676 | data[8 + strlen(filename)] = '\0'; | ||
| 677 | client->afc_packet->operation = AFC_FILE_OPEN; | ||
| 678 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
| 679 | bytes = dispatch_AFC_packet(client, data, 8 + strlen(filename)); | ||
| 680 | free(data); | ||
| 681 | |||
| 682 | if (bytes <= 0) { | ||
| 683 | log_debug_msg("afc_open_file: Didn't receive a response to the command\n"); | ||
| 684 | afc_unlock(client); | ||
| 685 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 686 | } | ||
| 687 | // Receive the data | ||
| 688 | length = receive_AFC_data(client, &data); | ||
| 689 | if (length > 0 && data) { | ||
| 690 | afc_unlock(client); | ||
| 691 | |||
| 692 | // Get the file info and return it | ||
| 693 | file_loc = afc_get_file_info(client, filename); | ||
| 694 | memcpy(&file_loc->filehandle, data, 4); | ||
| 695 | free(data); | ||
| 696 | *file = file_loc; | ||
| 697 | return IPHONE_E_SUCCESS; | ||
| 698 | } else { | ||
| 699 | log_debug_msg("afc_open_file: Didn't get any further data\n"); | ||
| 700 | afc_unlock(client); | ||
| 701 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 702 | } | ||
| 703 | |||
| 704 | afc_unlock(client); | ||
| 705 | |||
| 706 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 707 | } | ||
| 708 | |||
| 709 | /** Attempts to the read the given number of bytes from the given file. | ||
| 710 | * | ||
| 711 | * @param client The relevant AFC client | ||
| 712 | * @param file The AFCFile to read from | ||
| 713 | * @param data The pointer to the memory region to store the read data | ||
| 714 | * @param length The number of bytes to read | ||
| 715 | * | ||
| 716 | * @return The number of bytes read if successful. If there was an error -1. | ||
| 717 | */ | ||
| 718 | iphone_error_t | ||
| 719 | iphone_afc_read_file(iphone_afc_client_t client, iphone_afc_file_t file, char *data, int length, uint32_t * bytes) | ||
| 720 | { | ||
| 721 | char *input = NULL; | ||
| 722 | int current_count = 0, bytes_loc = 0; | ||
| 723 | const int MAXIMUM_READ_SIZE = 1 << 16; | ||
| 724 | |||
| 725 | if (!client || !client->afc_packet || !client->connection || !file) | ||
| 726 | return IPHONE_E_INVALID_ARG; | ||
| 727 | log_debug_msg("afc_read_file called for length %i\n", length); | ||
| 728 | |||
| 729 | afc_lock(client); | ||
| 730 | |||
| 731 | // Looping here to get around the maximum amount of data that | ||
| 732 | // recieve_AFC_data can handle | ||
| 733 | while (current_count < length) { | ||
| 734 | log_debug_msg("afc_read_file: current count is %i but length is %i\n", current_count, length); | ||
| 735 | |||
| 736 | // Send the read command | ||
| 737 | AFCFilePacket *packet = (AFCFilePacket *) malloc(sizeof(AFCFilePacket)); | ||
| 738 | packet->unknown1 = packet->unknown2 = 0; | ||
| 739 | packet->filehandle = file->filehandle; | ||
| 740 | packet->size = ((length - current_count) < MAXIMUM_READ_SIZE) ? (length - current_count) : MAXIMUM_READ_SIZE; | ||
| 741 | client->afc_packet->operation = AFC_READ; | ||
| 742 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
| 743 | bytes_loc = dispatch_AFC_packet(client, (char *) packet, sizeof(AFCFilePacket)); | ||
| 744 | free(packet); | ||
| 745 | |||
| 746 | if (bytes_loc <= 0) { | ||
| 747 | afc_unlock(client); | ||
| 748 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 749 | } | ||
| 750 | // Receive the data | ||
| 751 | bytes_loc = receive_AFC_data(client, &input); | ||
| 752 | log_debug_msg("afc_read_file: bytes returned: %i\n", bytes_loc); | ||
| 753 | if (bytes_loc < 0) { | ||
| 754 | if (input) | ||
| 755 | free(input); | ||
| 756 | afc_unlock(client); | ||
| 757 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 758 | } else if (bytes_loc == 0) { | ||
| 759 | if (input) | ||
| 760 | free(input); | ||
| 761 | afc_unlock(client); | ||
| 762 | *bytes = current_count; | ||
| 763 | return IPHONE_E_SUCCESS; // FIXME check that's actually a | ||
| 764 | // success | ||
| 765 | } else { | ||
| 766 | if (input) { | ||
| 767 | log_debug_msg("afc_read_file: %d\n", bytes_loc); | ||
| 768 | memcpy(data + current_count, input, (bytes_loc > length) ? length : bytes_loc); | ||
| 769 | free(input); | ||
| 770 | input = NULL; | ||
| 771 | current_count += (bytes_loc > length) ? length : bytes_loc; | ||
| 772 | } | ||
| 773 | } | ||
| 774 | } | ||
| 775 | log_debug_msg("afc_read_file: returning current_count as %i\n", current_count); | ||
| 776 | |||
| 777 | afc_unlock(client); | ||
| 778 | *bytes = current_count; | ||
| 779 | return IPHONE_E_SUCCESS; | ||
| 780 | } | ||
| 781 | |||
| 782 | /** Writes a given number of bytes to a file. | ||
| 783 | * | ||
| 784 | * @param client The client to use to write to the file. | ||
| 785 | * @param file A pointer to an AFCFile struct; serves as the file handle. | ||
| 786 | * @param data The data to write to the file. | ||
| 787 | * @param length How much data to write. | ||
| 788 | * | ||
| 789 | * @return The number of bytes written to the file, or a value less than 0 if | ||
| 790 | * none were written... | ||
| 791 | */ | ||
| 792 | iphone_error_t | ||
| 793 | iphone_afc_write_file(iphone_afc_client_t client, iphone_afc_file_t file, | ||
| 794 | const char *data, int length, uint32_t * bytes) | ||
| 795 | { | ||
| 796 | char *acknowledgement = NULL; | ||
| 797 | const int MAXIMUM_WRITE_SIZE = 1 << 15; | ||
| 798 | uint32_t zero = 0, bytes_loc = 0, segments = (length / MAXIMUM_WRITE_SIZE), current_count = 0, i = 0; | ||
| 799 | char *out_buffer = NULL; | ||
| 800 | |||
| 801 | if (!client || !client->afc_packet || !client->connection || !file || !bytes) | ||
| 802 | return IPHONE_E_INVALID_ARG; | ||
| 803 | |||
| 804 | afc_lock(client); | ||
| 805 | |||
| 806 | log_debug_msg("afc_write_file: Write length: %i\n", length); | ||
| 807 | |||
| 808 | // Divide the file into segments. | ||
| 809 | for (i = 0; i < segments; i++) { | ||
| 810 | // Send the segment | ||
| 811 | client->afc_packet->this_length = sizeof(AFCPacket) + 8; | ||
| 812 | client->afc_packet->entire_length = client->afc_packet->this_length + MAXIMUM_WRITE_SIZE; | ||
| 813 | client->afc_packet->operation = AFC_WRITE; | ||
| 814 | out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); | ||
| 815 | memcpy(out_buffer, (char *) &file->filehandle, sizeof(uint32_t)); | ||
| 816 | memcpy(out_buffer + 4, (char *) &zero, sizeof(uint32_t)); | ||
| 817 | memcpy(out_buffer + 8, data + current_count, MAXIMUM_WRITE_SIZE); | ||
| 818 | bytes_loc = dispatch_AFC_packet(client, out_buffer, MAXIMUM_WRITE_SIZE + 8); | ||
| 819 | if (bytes_loc < 0) { | ||
| 820 | afc_unlock(client); | ||
| 821 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 822 | } | ||
| 823 | free(out_buffer); | ||
| 824 | out_buffer = NULL; | ||
| 825 | |||
| 826 | current_count += bytes_loc; | ||
| 827 | bytes_loc = receive_AFC_data(client, &acknowledgement); | ||
| 828 | if (bytes_loc < 0) { | ||
| 829 | afc_unlock(client); | ||
| 830 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 831 | } | ||
| 832 | } | ||
| 833 | |||
| 834 | // By this point, we should be at the end. i.e. the last segment that | ||
| 835 | // didn't get sent in the for loop | ||
| 836 | // this length is fine because it's always sizeof(AFCPacket) + 8, but | ||
| 837 | // to be sure we do it again | ||
| 838 | if (current_count == length) { | ||
| 839 | afc_unlock(client); | ||
| 840 | *bytes = current_count; | ||
| 841 | return IPHONE_E_SUCCESS; | ||
| 842 | } | ||
| 843 | |||
| 844 | client->afc_packet->this_length = sizeof(AFCPacket) + 8; | ||
| 845 | client->afc_packet->entire_length = client->afc_packet->this_length + (length - current_count); | ||
| 846 | client->afc_packet->operation = AFC_WRITE; | ||
| 847 | out_buffer = (char *) malloc(sizeof(char) * client->afc_packet->entire_length - sizeof(AFCPacket)); | ||
| 848 | memcpy(out_buffer, (char *) &file->filehandle, sizeof(uint32_t)); | ||
| 849 | memcpy(out_buffer + 4, (char *) &zero, sizeof(uint32_t)); | ||
| 850 | memcpy(out_buffer + 8, data + current_count, (length - current_count)); | ||
| 851 | bytes_loc = dispatch_AFC_packet(client, out_buffer, (length - current_count) + 8); | ||
| 852 | free(out_buffer); | ||
| 853 | out_buffer = NULL; | ||
| 854 | |||
| 855 | current_count += bytes_loc; | ||
| 856 | |||
| 857 | if (bytes_loc <= 0) { | ||
| 858 | afc_unlock(client); | ||
| 859 | *bytes = current_count; | ||
| 860 | return IPHONE_E_SUCCESS; | ||
| 861 | } | ||
| 862 | |||
| 863 | zero = bytes_loc; | ||
| 864 | bytes_loc = receive_AFC_data(client, &acknowledgement); | ||
| 865 | afc_unlock(client); | ||
| 866 | if (bytes_loc < 0) { | ||
| 867 | log_debug_msg("afc_write_file: uh oh?\n"); | ||
| 868 | } | ||
| 869 | *bytes = current_count; | ||
| 870 | return IPHONE_E_SUCCESS; | ||
| 871 | } | ||
| 872 | |||
| 873 | /** Closes a file on the phone. | ||
| 874 | * | ||
| 875 | * @param client The client to close the file with. | ||
| 876 | * @param file A pointer to an AFCFile struct containing the file handle of the | ||
| 877 | * file to close. | ||
| 878 | */ | ||
| 879 | iphone_error_t iphone_afc_close_file(iphone_afc_client_t client, iphone_afc_file_t file) | ||
| 880 | { | ||
| 881 | if (!client || !file) | ||
| 882 | return IPHONE_E_INVALID_ARG; | ||
| 883 | char *buffer = malloc(sizeof(char) * 8); | ||
| 884 | uint32_t zero = 0; | ||
| 885 | int bytes = 0; | ||
| 886 | |||
| 887 | afc_lock(client); | ||
| 888 | |||
| 889 | log_debug_msg("afc_close_file: File handle %i\n", file->filehandle); | ||
| 890 | |||
| 891 | // Send command | ||
| 892 | memcpy(buffer, &file->filehandle, sizeof(uint32_t)); | ||
| 893 | memcpy(buffer + sizeof(uint32_t), &zero, sizeof(zero)); | ||
| 894 | client->afc_packet->operation = AFC_FILE_CLOSE; | ||
| 895 | client->afc_packet->entire_length = client->afc_packet->this_length = 0; | ||
| 896 | bytes = dispatch_AFC_packet(client, buffer, sizeof(char) * 8); | ||
| 897 | free(buffer); | ||
| 898 | buffer = NULL; | ||
| 899 | |||
| 900 | // FIXME: Is this necesary? | ||
| 901 | // client->afc_packet->entire_length = client->afc_packet->this_length | ||
| 902 | // = 0; | ||
| 903 | |||
| 904 | if (bytes <= 0) { | ||
| 905 | afc_unlock(client); | ||
| 906 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 907 | } | ||
| 908 | // Receive the response | ||
| 909 | bytes = receive_AFC_data(client, &buffer); | ||
| 910 | if (buffer) | ||
| 911 | free(buffer); | ||
| 912 | free(file); | ||
| 913 | afc_unlock(client); | ||
| 914 | return IPHONE_E_SUCCESS; | ||
| 915 | } | ||
| 916 | |||
| 917 | /** Seeks to a given position of a pre-opened file on the phone. | ||
| 918 | * | ||
| 919 | * @param client The client to use to seek to the position. | ||
| 920 | * @param file The file to seek to a position on. | ||
| 921 | * @param seekpos Where to seek to. If passed a negative value, this will seek | ||
| 922 | * from the end of the file. | ||
| 923 | * | ||
| 924 | * @return IPHONE_E_SUCCESS on success, IPHONE_E_NOT_ENOUGH_DATA on failure. | ||
| 925 | */ | ||
| 926 | iphone_error_t iphone_afc_seek_file(iphone_afc_client_t client, iphone_afc_file_t file, int seekpos) | ||
| 927 | { | ||
| 928 | char *buffer = (char *) malloc(sizeof(char) * 24); | ||
| 929 | uint32_t seekto = 0, bytes = 0, zero = 0; | ||
| 930 | |||
| 931 | if (seekpos < 0) | ||
| 932 | seekpos = file->size - abs(seekpos); | ||
| 933 | |||
| 934 | afc_lock(client); | ||
| 935 | |||
| 936 | // Send the command | ||
| 937 | seekto = seekpos; | ||
| 938 | memcpy(buffer, &file->filehandle, sizeof(uint32_t)); // handle | ||
| 939 | memcpy(buffer + 4, &zero, sizeof(uint32_t)); // pad | ||
| 940 | memcpy(buffer + 8, &zero, sizeof(uint32_t)); // fromwhere | ||
| 941 | memcpy(buffer + 12, &zero, sizeof(uint32_t)); // pad | ||
| 942 | memcpy(buffer + 16, &seekto, sizeof(uint32_t)); // offset | ||
| 943 | memcpy(buffer + 20, &zero, sizeof(uint32_t)); // pad | ||
| 944 | client->afc_packet->operation = AFC_FILE_SEEK; | ||
| 945 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | ||
| 946 | bytes = dispatch_AFC_packet(client, buffer, 23); | ||
| 947 | free(buffer); | ||
| 948 | buffer = NULL; | ||
| 949 | |||
| 950 | if (bytes <= 0) { | ||
| 951 | afc_unlock(client); | ||
| 952 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 953 | } | ||
| 954 | // Receive response | ||
| 955 | bytes = receive_AFC_data(client, &buffer); | ||
| 956 | if (buffer) | ||
| 957 | free(buffer); | ||
| 958 | |||
| 959 | afc_unlock(client); | ||
| 960 | |||
| 961 | if (bytes >= 0) { | ||
| 962 | return IPHONE_E_SUCCESS; | ||
| 963 | } else { | ||
| 964 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 965 | } | ||
| 966 | } | ||
| 967 | |||
| 968 | /** Sets the size of a file on the phone. | ||
| 969 | * | ||
| 970 | * @param client The client to use to set the file size. | ||
| 971 | * @param file The (pre-opened) file to set the size on. | ||
| 972 | * @param newsize The size to set the file to. | ||
| 973 | * | ||
| 974 | * @return 0 on success, -1 on failure. | ||
| 975 | * | ||
| 976 | * @note This function is more akin to ftruncate than truncate, and truncate | ||
| 977 | * calls would have to open the file before calling this, sadly. | ||
| 978 | */ | ||
| 979 | iphone_error_t iphone_afc_truncate_file(iphone_afc_client_t client, iphone_afc_file_t file, uint32_t newsize) | ||
| 980 | { | ||
| 981 | char *buffer = (char *) malloc(sizeof(char) * 16); | ||
| 982 | uint32_t bytes = 0, zero = 0; | ||
| 983 | |||
| 984 | afc_lock(client); | ||
| 985 | |||
| 986 | // Send command | ||
| 987 | memcpy(buffer, &file->filehandle, sizeof(uint32_t)); // handle | ||
| 988 | memcpy(buffer + 4, &zero, sizeof(uint32_t)); // pad | ||
| 989 | memcpy(buffer + 8, &newsize, sizeof(uint32_t)); // newsize | ||
| 990 | memcpy(buffer + 12, &zero, 3); // pad | ||
| 991 | client->afc_packet->operation = AFC_FILE_TRUNCATE; | ||
| 992 | client->afc_packet->this_length = client->afc_packet->entire_length = 0; | ||
| 993 | bytes = dispatch_AFC_packet(client, buffer, 15); | ||
| 994 | free(buffer); | ||
| 995 | buffer = NULL; | ||
| 996 | |||
| 997 | if (bytes <= 0) { | ||
| 998 | afc_unlock(client); | ||
| 999 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 1000 | } | ||
| 1001 | // Receive response | ||
| 1002 | bytes = receive_AFC_data(client, &buffer); | ||
| 1003 | if (buffer) | ||
| 1004 | free(buffer); | ||
| 1005 | |||
| 1006 | afc_unlock(client); | ||
| 1007 | |||
| 1008 | if (bytes >= 0) { | ||
| 1009 | return IPHONE_E_SUCCESS; | ||
| 1010 | } else { | ||
| 1011 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | uint32_t iphone_afc_get_file_handle(iphone_afc_file_t file) | ||
| 1016 | { | ||
| 1017 | return file->filehandle; | ||
| 1018 | } | ||
diff --git a/src/AFC.h b/src/AFC.h deleted file mode 100644 index 5e4d17c..0000000 --- a/src/AFC.h +++ /dev/null | |||
| @@ -1,77 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * AFC.h | ||
| 3 | * Defines and structs and the like for the built-in AFC client | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "usbmux.h" | ||
| 23 | #include "iphone.h" | ||
| 24 | |||
| 25 | #include <string.h> | ||
| 26 | #include <stdio.h> | ||
| 27 | #include <stdlib.h> | ||
| 28 | #include <sys/stat.h> | ||
| 29 | #include <glib.h> | ||
| 30 | |||
| 31 | typedef struct { | ||
| 32 | uint32_t header1, header2; | ||
| 33 | uint32_t entire_length, unknown1, this_length, unknown2, packet_num, unknown3, operation, unknown4; | ||
| 34 | } AFCPacket; | ||
| 35 | |||
| 36 | typedef struct { | ||
| 37 | uint32_t filehandle, unknown1, size, unknown2; | ||
| 38 | } AFCFilePacket; | ||
| 39 | |||
| 40 | typedef struct __AFCToken { | ||
| 41 | struct __AFCToken *last, *next; | ||
| 42 | char *token; | ||
| 43 | } AFCToken; | ||
| 44 | |||
| 45 | struct iphone_afc_client_int { | ||
| 46 | iphone_umux_client_t connection; | ||
| 47 | AFCPacket *afc_packet; | ||
| 48 | int file_handle; | ||
| 49 | int lock; | ||
| 50 | GMutex *mutex; | ||
| 51 | }; | ||
| 52 | |||
| 53 | struct iphone_afc_file_int { | ||
| 54 | uint32_t filehandle, blocks, size, type; | ||
| 55 | }; | ||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | enum { | ||
| 60 | AFC_ERROR = 0x00000001, | ||
| 61 | AFC_GET_INFO = 0x0000000a, | ||
| 62 | AFC_GET_DEVINFO = 0x0000000b, | ||
| 63 | AFC_LIST_DIR = 0x00000003, | ||
| 64 | AFC_MAKE_DIR = 0x00000009, | ||
| 65 | AFC_DELETE = 0x00000008, | ||
| 66 | AFC_RENAME = 0x00000018, | ||
| 67 | AFC_SUCCESS_RESPONSE = 0x00000002, | ||
| 68 | AFC_FILE_OPEN = 0x0000000d, | ||
| 69 | AFC_FILE_CLOSE = 0x00000014, | ||
| 70 | AFC_FILE_SEEK = 0x00000011, | ||
| 71 | AFC_FILE_TRUNCATE = 0x00000015, | ||
| 72 | AFC_FILE_HANDLE = 0x0000000e, | ||
| 73 | AFC_READ = 0x0000000f, | ||
| 74 | AFC_WRITE = 0x00000010 | ||
| 75 | }; | ||
| 76 | |||
| 77 | uint32_t iphone_afc_get_file_handle(iphone_afc_file_t file); | ||
diff --git a/src/Makefile.am b/src/Makefile.am index 82fd924..1b81710 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
| @@ -1,15 +1,7 @@ | |||
| 1 | INCLUDES = -I$(top_srcdir)/include | 1 | INCLUDES = -I$(top_srcdir)/include |
| 2 | 2 | ||
| 3 | AM_CFLAGS = $(libxml2_CFLAGS) $(libusb_CFLAGS) $(libglib2_CFLAGS) $(libgnutls_CFLAGS) $(libtasn1_CFLAGS) $(libgthread2_CFLAGS) -g | 3 | AM_CFLAGS = $(libxml2_CFLAGS) $(libglib2_CFLAGS) |
| 4 | AM_LDFLAGS = $(libxml2_LIBS) $(libusb_LIBS) $(libglib2_LIBS) $(libgnutls_LIBS) $(libtasn1_LIBS) $(libgthread2_LIBS) | 4 | AM_LDFLAGS = $(libxml2_LIBS) $(libglib2_LIBS) |
| 5 | 5 | ||
| 6 | bin_PROGRAMS = libiphone-initconf | 6 | lib_LTLIBRARIES = libplist.la |
| 7 | 7 | libplist_la_SOURCES = plist.c bplist.c xplist.c utils.c | |
| 8 | |||
| 9 | libiphone_initconf_SOURCES = initconf.c userpref.c utils.c | ||
| 10 | libiphone_initconf_CFLAGS = $(libgthread2_CFLAGS) $(AM_CFLAGS) | ||
| 11 | libiphone_initconf_LDFLAGS = $(libgthread2_LIBS) $(AM_LDFLAGS) | ||
| 12 | |||
| 13 | |||
| 14 | lib_LTLIBRARIES = libiphone.la | ||
| 15 | libiphone_la_SOURCES = usbmux.c iphone.c plist.c bplist.c xplist.c lockdown.c AFC.c userpref.c utils.c | ||
diff --git a/src/initconf.c b/src/initconf.c deleted file mode 100644 index 00d78e2..0000000 --- a/src/initconf.c +++ /dev/null | |||
| @@ -1,213 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * userpref.c | ||
| 3 | * contains methods to access user specific certificates IDs and more. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Jonathan Beck All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <stdio.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #include <string.h> | ||
| 25 | #include <gnutls/gnutls.h> | ||
| 26 | #include <gnutls/x509.h> | ||
| 27 | #include <glib.h> | ||
| 28 | |||
| 29 | #include "libiphone/libiphone.h" | ||
| 30 | #include "userpref.h" | ||
| 31 | #include "utils.h" | ||
| 32 | |||
| 33 | /** Generates a 2048 byte key, split into a function so that it can be run in a | ||
| 34 | * thread. | ||
| 35 | * | ||
| 36 | * @param key The pointer to the desired location of the new key. | ||
| 37 | */ | ||
| 38 | void generate_key(gpointer key) | ||
| 39 | { | ||
| 40 | gnutls_x509_privkey_generate(*((gnutls_x509_privkey_t *) key), GNUTLS_PK_RSA, 2048, 0); | ||
| 41 | g_thread_exit(0); | ||
| 42 | } | ||
| 43 | |||
| 44 | /** Simple function that generates a spinner until the mutex is released. | ||
| 45 | */ | ||
| 46 | void progress_bar(gpointer mutex) | ||
| 47 | { | ||
| 48 | const char *spinner = "|/-\\|/-\\"; | ||
| 49 | int i = 0; | ||
| 50 | |||
| 51 | while (!g_static_mutex_trylock((GStaticMutex *) mutex)) { | ||
| 52 | usleep(500000); | ||
| 53 | printf("Generating key... %c\r", spinner[i++]); | ||
| 54 | fflush(stdout); | ||
| 55 | if (i > 8) | ||
| 56 | i = 0; | ||
| 57 | } | ||
| 58 | printf("Generating key... done\n"); | ||
| 59 | g_thread_exit(0); | ||
| 60 | } | ||
| 61 | |||
| 62 | int get_rand(int min, int max) | ||
| 63 | { | ||
| 64 | int retval = (rand() % (max - min)) + min; | ||
| 65 | return retval; | ||
| 66 | } | ||
| 67 | |||
| 68 | /** Generates a valid HostID (which is actually a UUID). | ||
| 69 | * | ||
| 70 | * @param A null terminated string containing a valid HostID. | ||
| 71 | */ | ||
| 72 | char *lockdownd_generate_hostid() | ||
| 73 | { | ||
| 74 | char *hostid = (char *) malloc(sizeof(char) * 37); // HostID's are just UUID's, and UUID's are 36 characters long | ||
| 75 | const char *chars = "ABCDEF0123456789"; | ||
| 76 | srand(time(NULL)); | ||
| 77 | int i = 0; | ||
| 78 | |||
| 79 | for (i = 0; i < 36; i++) { | ||
| 80 | if (i == 8 || i == 13 || i == 18 || i == 23) { | ||
| 81 | hostid[i] = '-'; | ||
| 82 | continue; | ||
| 83 | } else { | ||
| 84 | hostid[i] = chars[get_rand(0, 16)]; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | hostid[36] = '\0'; // make it a real string | ||
| 88 | return hostid; | ||
| 89 | } | ||
| 90 | |||
| 91 | int main(int argc, char *argv[]) | ||
| 92 | { | ||
| 93 | GThread *progress_thread, *key_thread; | ||
| 94 | GError *err; | ||
| 95 | static GStaticMutex mutex = G_STATIC_MUTEX_INIT; | ||
| 96 | char *host_id = NULL; | ||
| 97 | gnutls_x509_privkey_t root_privkey; | ||
| 98 | gnutls_x509_privkey_t host_privkey; | ||
| 99 | gnutls_x509_crt_t root_cert; | ||
| 100 | gnutls_x509_crt_t host_cert; | ||
| 101 | |||
| 102 | iphone_set_debug(1); | ||
| 103 | |||
| 104 | // Create the thread | ||
| 105 | if (!g_thread_supported()) { | ||
| 106 | g_thread_init(NULL); | ||
| 107 | } | ||
| 108 | gnutls_global_init(); | ||
| 109 | |||
| 110 | printf("This program generates keys required to connect with the iPhone\n"); | ||
| 111 | printf("It only needs to be run ONCE.\n\n"); | ||
| 112 | printf("Additionally it may take several minutes to run, please be patient.\n\n"); | ||
| 113 | |||
| 114 | |||
| 115 | gnutls_x509_privkey_init(&root_privkey); | ||
| 116 | gnutls_x509_privkey_init(&host_privkey); | ||
| 117 | |||
| 118 | gnutls_x509_crt_init(&root_cert); | ||
| 119 | gnutls_x509_crt_init(&host_cert); | ||
| 120 | |||
| 121 | /* generate HostID */ | ||
| 122 | host_id = lockdownd_generate_hostid(); | ||
| 123 | |||
| 124 | /* generate root key */ | ||
| 125 | g_static_mutex_lock(&mutex); | ||
| 126 | if ((key_thread = g_thread_create((GThreadFunc) generate_key, &root_privkey, TRUE, &err)) == NULL) { | ||
| 127 | printf("Thread create failed: %s!!\n", err->message); | ||
| 128 | g_error_free(err); | ||
| 129 | } | ||
| 130 | if ((progress_thread = g_thread_create((GThreadFunc) progress_bar, &mutex, TRUE, &err)) == NULL) { | ||
| 131 | printf("Thread create failed: %s!!\n", err->message); | ||
| 132 | g_error_free(err); | ||
| 133 | } | ||
| 134 | g_thread_join(key_thread); | ||
| 135 | g_static_mutex_unlock(&mutex); | ||
| 136 | g_thread_join(progress_thread); | ||
| 137 | |||
| 138 | /* generate host key */ | ||
| 139 | g_static_mutex_init(&mutex); | ||
| 140 | g_static_mutex_lock(&mutex); | ||
| 141 | if ((key_thread = g_thread_create((GThreadFunc) generate_key, &host_privkey, TRUE, &err)) == NULL) { | ||
| 142 | printf("Thread create failed: %s!!\n", err->message); | ||
| 143 | g_error_free(err); | ||
| 144 | } | ||
| 145 | if ((progress_thread = g_thread_create((GThreadFunc) progress_bar, &mutex, TRUE, &err)) == NULL) { | ||
| 146 | printf("Thread create failed: %s!!\n", err->message); | ||
| 147 | g_error_free(err); | ||
| 148 | } | ||
| 149 | g_thread_join(key_thread); | ||
| 150 | g_static_mutex_unlock(&mutex); | ||
| 151 | g_thread_join(progress_thread); | ||
| 152 | |||
| 153 | /* generate certificates */ | ||
| 154 | gnutls_x509_crt_set_key(root_cert, root_privkey); | ||
| 155 | gnutls_x509_crt_set_serial(root_cert, "\x00", 1); | ||
| 156 | gnutls_x509_crt_set_version(root_cert, 3); | ||
| 157 | gnutls_x509_crt_set_ca_status(root_cert, 1); | ||
| 158 | gnutls_x509_crt_set_activation_time(root_cert, time(NULL)); | ||
| 159 | gnutls_x509_crt_set_expiration_time(root_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
| 160 | gnutls_x509_crt_sign(root_cert, root_cert, root_privkey); | ||
| 161 | |||
| 162 | |||
| 163 | gnutls_x509_crt_set_key(host_cert, host_privkey); | ||
| 164 | gnutls_x509_crt_set_serial(host_cert, "\x00", 1); | ||
| 165 | gnutls_x509_crt_set_version(host_cert, 3); | ||
| 166 | gnutls_x509_crt_set_ca_status(host_cert, 0); | ||
| 167 | gnutls_x509_crt_set_key_usage(host_cert, GNUTLS_KEY_KEY_ENCIPHERMENT | GNUTLS_KEY_DIGITAL_SIGNATURE); | ||
| 168 | gnutls_x509_crt_set_activation_time(host_cert, time(NULL)); | ||
| 169 | gnutls_x509_crt_set_expiration_time(host_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
| 170 | gnutls_x509_crt_sign(host_cert, root_cert, root_privkey); | ||
| 171 | |||
| 172 | |||
| 173 | /* export to PEM format */ | ||
| 174 | gnutls_datum_t root_key_pem = { NULL, 0 }; | ||
| 175 | gnutls_datum_t host_key_pem = { NULL, 0 }; | ||
| 176 | |||
| 177 | gnutls_x509_privkey_export(root_privkey, GNUTLS_X509_FMT_PEM, NULL, &root_key_pem.size); | ||
| 178 | gnutls_x509_privkey_export(host_privkey, GNUTLS_X509_FMT_PEM, NULL, &host_key_pem.size); | ||
| 179 | |||
| 180 | root_key_pem.data = gnutls_malloc(root_key_pem.size); | ||
| 181 | host_key_pem.data = gnutls_malloc(host_key_pem.size); | ||
| 182 | |||
| 183 | gnutls_x509_privkey_export(root_privkey, GNUTLS_X509_FMT_PEM, root_key_pem.data, &root_key_pem.size); | ||
| 184 | gnutls_x509_privkey_export(host_privkey, GNUTLS_X509_FMT_PEM, host_key_pem.data, &host_key_pem.size); | ||
| 185 | |||
| 186 | gnutls_datum_t root_cert_pem = { NULL, 0 }; | ||
| 187 | gnutls_datum_t host_cert_pem = { NULL, 0 }; | ||
| 188 | |||
| 189 | gnutls_x509_crt_export(root_cert, GNUTLS_X509_FMT_PEM, NULL, &root_cert_pem.size); | ||
| 190 | gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, NULL, &host_cert_pem.size); | ||
| 191 | |||
| 192 | root_cert_pem.data = gnutls_malloc(root_cert_pem.size); | ||
| 193 | host_cert_pem.data = gnutls_malloc(host_cert_pem.size); | ||
| 194 | |||
| 195 | printf("Generating root certificate..."); | ||
| 196 | gnutls_x509_crt_export(root_cert, GNUTLS_X509_FMT_PEM, root_cert_pem.data, &root_cert_pem.size); | ||
| 197 | printf("done\n"); | ||
| 198 | |||
| 199 | printf("Generating host certificate..."); | ||
| 200 | gnutls_x509_crt_export(host_cert, GNUTLS_X509_FMT_PEM, host_cert_pem.data, &host_cert_pem.size); | ||
| 201 | printf("done\n"); | ||
| 202 | |||
| 203 | |||
| 204 | /* store values in config file */ | ||
| 205 | init_config_file(host_id, &root_key_pem, &host_key_pem, &root_cert_pem, &host_cert_pem); | ||
| 206 | |||
| 207 | gnutls_free(root_key_pem.data); | ||
| 208 | gnutls_free(host_key_pem.data); | ||
| 209 | gnutls_free(root_cert_pem.data); | ||
| 210 | gnutls_free(host_cert_pem.data); | ||
| 211 | |||
| 212 | return 0; | ||
| 213 | } | ||
diff --git a/src/iphone.c b/src/iphone.c deleted file mode 100644 index b7f6cc4..0000000 --- a/src/iphone.c +++ /dev/null | |||
| @@ -1,247 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * iphone.c | ||
| 3 | * Functions for creating and initializing iPhone structures. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "usbmux.h" | ||
| 23 | #include "iphone.h" | ||
| 24 | #include "utils.h" | ||
| 25 | #include <arpa/inet.h> | ||
| 26 | #include <usb.h> | ||
| 27 | #include <stdio.h> | ||
| 28 | #include <stdlib.h> | ||
| 29 | #include <string.h> | ||
| 30 | |||
| 31 | |||
| 32 | /** | ||
| 33 | * Given a USB bus and device number, returns a device handle to the iPhone on | ||
| 34 | * that bus. To aid compatibility with future devices, this function does not | ||
| 35 | * check the vendor and device IDs! To do that, you should use | ||
| 36 | * iphone_get_device() or a system-specific API (e.g. HAL). | ||
| 37 | * | ||
| 38 | * @param bus_n The USB bus number. | ||
| 39 | * @param dev_n The USB device number. | ||
| 40 | * @param device A pointer to a iphone_device_t, which must be set to NULL upon | ||
| 41 | * calling iphone_get_specific_device, which will be filled with a device | ||
| 42 | * descriptor on return. | ||
| 43 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | ||
| 44 | */ | ||
| 45 | iphone_error_t iphone_get_specific_device(int bus_n, int dev_n, iphone_device_t * device) | ||
| 46 | { | ||
| 47 | struct usb_bus *bus, *busses; | ||
| 48 | struct usb_device *dev; | ||
| 49 | usbmux_version_header *version; | ||
| 50 | int bytes = 0; | ||
| 51 | |||
| 52 | //check we can actually write in device | ||
| 53 | if (!device || (device && *device)) | ||
| 54 | return IPHONE_E_INVALID_ARG; | ||
| 55 | |||
| 56 | iphone_device_t phone = (iphone_device_t) malloc(sizeof(struct iphone_device_int)); | ||
| 57 | |||
| 58 | // Initialize the struct | ||
| 59 | phone->device = NULL; | ||
| 60 | phone->__device = NULL; | ||
| 61 | phone->buffer = NULL; | ||
| 62 | |||
| 63 | // Initialize libusb | ||
| 64 | usb_init(); | ||
| 65 | usb_find_busses(); | ||
| 66 | usb_find_devices(); | ||
| 67 | busses = usb_get_busses(); | ||
| 68 | |||
| 69 | // Set the device configuration | ||
| 70 | for (bus = busses; bus; bus = bus->next) | ||
| 71 | if (bus->location == bus_n) | ||
| 72 | for (dev = bus->devices; dev != NULL; dev = dev->next) | ||
| 73 | if (dev->devnum == dev_n) { | ||
| 74 | phone->__device = dev; | ||
| 75 | phone->device = usb_open(phone->__device); | ||
| 76 | usb_set_configuration(phone->device, 3); | ||
| 77 | usb_claim_interface(phone->device, 1); | ||
| 78 | goto found; | ||
| 79 | } | ||
| 80 | |||
| 81 | iphone_free_device(phone); | ||
| 82 | |||
| 83 | log_debug_msg("iphone_get_specific_device: iPhone not found\n"); | ||
| 84 | return IPHONE_E_NO_DEVICE; | ||
| 85 | |||
| 86 | found: | ||
| 87 | // Send the version command to the phone | ||
| 88 | version = version_header(); | ||
| 89 | bytes = usb_bulk_write(phone->device, BULKOUT, (char *) version, sizeof(*version), 800); | ||
| 90 | if (bytes < 20) { | ||
| 91 | log_debug_msg("get_iPhone(): libusb did NOT send enough!\n"); | ||
| 92 | if (bytes < 0) { | ||
| 93 | log_debug_msg("get_iPhone(): libusb gave me the error %d: %s (%s)\n", | ||
| 94 | bytes, usb_strerror(), strerror(-bytes)); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | // Read the phone's response | ||
| 98 | bytes = usb_bulk_read(phone->device, BULKIN, (char *) version, sizeof(*version), 800); | ||
| 99 | |||
| 100 | // Check for bad response | ||
| 101 | if (bytes < 20) { | ||
| 102 | free(version); | ||
| 103 | iphone_free_device(phone); | ||
| 104 | log_debug_msg("get_iPhone(): Invalid version message -- header too short.\n"); | ||
| 105 | if (bytes < 0) | ||
| 106 | log_debug_msg("get_iPhone(): libusb error message %d: %s (%s)\n", bytes, usb_strerror(), strerror(-bytes)); | ||
| 107 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 108 | } | ||
| 109 | // Check for correct version | ||
| 110 | if (ntohl(version->major) == 1 && ntohl(version->minor) == 0) { | ||
| 111 | // We're all ready to roll. | ||
| 112 | fprintf(stderr, "get_iPhone() success\n"); | ||
| 113 | free(version); | ||
| 114 | *device = phone; | ||
| 115 | return IPHONE_E_SUCCESS; | ||
| 116 | } else { | ||
| 117 | // Bad header | ||
| 118 | iphone_free_device(phone); | ||
| 119 | free(version); | ||
| 120 | log_debug_msg("get_iPhone(): Received a bad header/invalid version number."); | ||
| 121 | return IPHONE_E_BAD_HEADER; | ||
| 122 | } | ||
| 123 | |||
| 124 | // If it got to this point it's gotta be bad | ||
| 125 | log_debug_msg("get_iPhone(): Unknown error.\n"); | ||
| 126 | iphone_free_device(phone); | ||
| 127 | free(version); | ||
| 128 | return IPHONE_E_UNKNOWN_ERROR; // if it got to this point it's gotta be bad | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * Scans all USB busses and devices for a known AFC-compatible device and | ||
| 133 | * returns a handle to the first such device it finds. Known devices include | ||
| 134 | * those with vendor ID 0x05ac and product ID between 0x1290 and 0x1293 | ||
| 135 | * inclusive. | ||
| 136 | * | ||
| 137 | * This function is convenient, but on systems where higher-level abstractions | ||
| 138 | * (such as HAL) are available it may be preferable to use | ||
| 139 | * iphone_get_specific_device instead, because it can deal with multiple | ||
| 140 | * connected devices as well as devices not known to libiphone. | ||
| 141 | * | ||
| 142 | * @param device Upon calling this function, a pointer to a location of type | ||
| 143 | * iphone_device_t, which must have the value NULL. On return, this location | ||
| 144 | * will be filled with a handle to the device. | ||
| 145 | * @return IPHONE_E_SUCCESS if ok, otherwise an error code. | ||
| 146 | */ | ||
| 147 | iphone_error_t iphone_get_device(iphone_device_t * device) | ||
| 148 | { | ||
| 149 | struct usb_bus *bus, *busses; | ||
| 150 | struct usb_device *dev; | ||
| 151 | |||
| 152 | usb_init(); | ||
| 153 | usb_find_busses(); | ||
| 154 | usb_find_devices(); | ||
| 155 | |||
| 156 | for (bus = usb_get_busses(); bus != NULL; bus = bus->next) | ||
| 157 | for (dev = bus->devices; dev != NULL; dev = dev->next) | ||
| 158 | if (dev->descriptor.idVendor == 0x05ac | ||
| 159 | && dev->descriptor.idProduct >= 0x1290 && dev->descriptor.idProduct <= 0x1293) | ||
| 160 | return iphone_get_specific_device(bus->location, dev->devnum, device); | ||
| 161 | |||
| 162 | return IPHONE_E_NO_DEVICE; | ||
| 163 | } | ||
| 164 | |||
| 165 | /** Cleans up an iPhone structure, then frees the structure itself. | ||
| 166 | * This is a library-level function; deals directly with the iPhone to tear | ||
| 167 | * down relations, but otherwise is mostly internal. | ||
| 168 | * | ||
| 169 | * @param phone A pointer to an iPhone structure. | ||
| 170 | */ | ||
| 171 | iphone_error_t iphone_free_device(iphone_device_t device) | ||
| 172 | { | ||
| 173 | if (!device) | ||
| 174 | return IPHONE_E_INVALID_ARG; | ||
| 175 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 176 | |||
| 177 | if (device->buffer) { | ||
| 178 | free(device->buffer); | ||
| 179 | } | ||
| 180 | if (device->device) { | ||
| 181 | usb_release_interface(device->device, 1); | ||
| 182 | usb_reset(device->device); | ||
| 183 | usb_close(device->device); | ||
| 184 | ret = IPHONE_E_SUCCESS; | ||
| 185 | } | ||
| 186 | free(device); | ||
| 187 | return ret; | ||
| 188 | } | ||
| 189 | |||
| 190 | /** Sends data to the phone | ||
| 191 | * This is a low-level (i.e. directly to phone) function. | ||
| 192 | * | ||
| 193 | * @param phone The iPhone to send data to | ||
| 194 | * @param data The data to send to the iPhone | ||
| 195 | * @param datalen The length of the data | ||
| 196 | * @return The number of bytes sent, or -1 on error or something. | ||
| 197 | */ | ||
| 198 | int send_to_phone(iphone_device_t phone, char *data, int datalen) | ||
| 199 | { | ||
| 200 | if (!phone) | ||
| 201 | return -1; | ||
| 202 | int bytes = 0; | ||
| 203 | |||
| 204 | if (!phone) | ||
| 205 | return -1; | ||
| 206 | log_debug_msg("send_to_phone: Attempting to send datalen = %i data = %p\n", datalen, data); | ||
| 207 | |||
| 208 | bytes = usb_bulk_write(phone->device, BULKOUT, data, datalen, 800); | ||
| 209 | if (bytes < datalen) { | ||
| 210 | if (bytes < 0) | ||
| 211 | log_debug_msg("send_to_iphone(): libusb gave me the error %d: %s - %s\n", bytes, usb_strerror(), | ||
| 212 | strerror(-bytes)); | ||
| 213 | return -1; | ||
| 214 | } else { | ||
| 215 | return bytes; | ||
| 216 | } | ||
| 217 | |||
| 218 | return -1; | ||
| 219 | } | ||
| 220 | |||
| 221 | /** This function is a low-level (i.e. direct to iPhone) function. | ||
| 222 | * | ||
| 223 | * @param phone The iPhone to receive data from | ||
| 224 | * @param data Where to put data read | ||
| 225 | * @param datalen How much data to read in | ||
| 226 | * | ||
| 227 | * @return How many bytes were read in, or -1 on error. | ||
| 228 | */ | ||
| 229 | int recv_from_phone(iphone_device_t phone, char *data, int datalen) | ||
| 230 | { | ||
| 231 | if (!phone) | ||
| 232 | return -1; | ||
| 233 | int bytes = 0; | ||
| 234 | |||
| 235 | if (!phone) | ||
| 236 | return -1; | ||
| 237 | log_debug_msg("recv_from_phone(): attempting to receive %i bytes\n", datalen); | ||
| 238 | |||
| 239 | bytes = usb_bulk_read(phone->device, BULKIN, data, datalen, 3500); | ||
| 240 | if (bytes < 0) { | ||
| 241 | log_debug_msg("recv_from_phone(): libusb gave me the error %d: %s (%s)\n", bytes, usb_strerror(), | ||
| 242 | strerror(-bytes)); | ||
| 243 | return -1; | ||
| 244 | } | ||
| 245 | |||
| 246 | return bytes; | ||
| 247 | } | ||
diff --git a/src/iphone.h b/src/iphone.h deleted file mode 100644 index 222a1be..0000000 --- a/src/iphone.h +++ /dev/null | |||
| @@ -1,45 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * iphone.h | ||
| 3 | * iPhone struct | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #ifndef IPHONE_H | ||
| 23 | #define IPHONE_H | ||
| 24 | |||
| 25 | #ifndef USBMUX_H | ||
| 26 | #include "usbmux.h" | ||
| 27 | #warning usbmux not included? | ||
| 28 | #endif | ||
| 29 | |||
| 30 | #include <usb.h> | ||
| 31 | #include <libiphone/libiphone.h> | ||
| 32 | |||
| 33 | #define BULKIN 0x85 | ||
| 34 | #define BULKOUT 0x04 | ||
| 35 | |||
| 36 | struct iphone_device_int { | ||
| 37 | char *buffer; | ||
| 38 | struct usb_dev_handle *device; | ||
| 39 | struct usb_device *__device; | ||
| 40 | }; | ||
| 41 | |||
| 42 | // Function definitions | ||
| 43 | int send_to_phone(iphone_device_t phone, char *data, int datalen); | ||
| 44 | int recv_from_phone(iphone_device_t phone, char *data, int datalen); | ||
| 45 | #endif | ||
diff --git a/src/lockdown.c b/src/lockdown.c deleted file mode 100644 index e882128..0000000 --- a/src/lockdown.c +++ /dev/null | |||
| @@ -1,969 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * lockdown.c | ||
| 3 | * libiphone built-in lockdownd client | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include "usbmux.h" | ||
| 23 | #include "iphone.h" | ||
| 24 | #include "lockdown.h" | ||
| 25 | #include "userpref.h" | ||
| 26 | #include <arpa/inet.h> | ||
| 27 | #include <errno.h> | ||
| 28 | #include <string.h> | ||
| 29 | #include <glib.h> | ||
| 30 | #include <libtasn1.h> | ||
| 31 | #include <gnutls/x509.h> | ||
| 32 | |||
| 33 | const ASN1_ARRAY_TYPE pkcs1_asn1_tab[] = { | ||
| 34 | {"PKCS1", 536872976, 0}, | ||
| 35 | {0, 1073741836, 0}, | ||
| 36 | {"RSAPublicKey", 536870917, 0}, | ||
| 37 | {"modulus", 1073741827, 0}, | ||
| 38 | {"publicExponent", 3, 0}, | ||
| 39 | {0, 0, 0} | ||
| 40 | }; | ||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | /** Creates a lockdownd client for the give iPhone. | ||
| 45 | * | ||
| 46 | * @param phone The iPhone to create a lockdownd client for | ||
| 47 | * | ||
| 48 | * @return The lockdownd client. | ||
| 49 | */ | ||
| 50 | iphone_lckd_client_t new_lockdownd_client(iphone_device_t phone) | ||
| 51 | { | ||
| 52 | if (!phone) | ||
| 53 | return NULL; | ||
| 54 | iphone_lckd_client_t control = (iphone_lckd_client_t) malloc(sizeof(struct iphone_lckd_client_int)); | ||
| 55 | |||
| 56 | if (IPHONE_E_SUCCESS != iphone_mux_new_client(phone, 0x0a00, 0xf27e, &control->connection)) { | ||
| 57 | free(control); | ||
| 58 | return NULL; | ||
| 59 | } | ||
| 60 | |||
| 61 | control->ssl_session = (gnutls_session_t *) malloc(sizeof(gnutls_session_t)); | ||
| 62 | control->in_SSL = 0; | ||
| 63 | control->gtls_buffer_hack_len = 0; | ||
| 64 | return control; | ||
| 65 | } | ||
| 66 | |||
| 67 | /** Closes the lockdownd client and does the necessary housekeeping. | ||
| 68 | * | ||
| 69 | * @param control The lockdown client | ||
| 70 | */ | ||
| 71 | iphone_error_t iphone_lckd_free_client(iphone_lckd_client_t client) | ||
| 72 | { | ||
| 73 | if (!client) | ||
| 74 | return IPHONE_E_INVALID_ARG; | ||
| 75 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 76 | |||
| 77 | if (client->connection) { | ||
| 78 | ret = iphone_mux_free_client(client->connection); | ||
| 79 | } | ||
| 80 | |||
| 81 | if (client->ssl_session) | ||
| 82 | gnutls_deinit(*client->ssl_session); | ||
| 83 | free(client->ssl_session); | ||
| 84 | free(client); | ||
| 85 | return ret; | ||
| 86 | } | ||
| 87 | |||
| 88 | /** Polls the iPhone for lockdownd data. | ||
| 89 | * | ||
| 90 | * @param control The lockdownd client | ||
| 91 | * @param dump_data The pointer to the location of the buffer in which to store | ||
| 92 | * the received data | ||
| 93 | * | ||
| 94 | * @return The number of bytes received | ||
| 95 | */ | ||
| 96 | iphone_error_t iphone_lckd_recv(iphone_lckd_client_t client, char **dump_data, uint32_t * recv_bytes) | ||
| 97 | { | ||
| 98 | if (!client || !dump_data || !recv_bytes) | ||
| 99 | return IPHONE_E_INVALID_ARG; | ||
| 100 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 101 | char *receive; | ||
| 102 | uint32_t datalen = 0, bytes = 0; | ||
| 103 | |||
| 104 | if (!client->in_SSL) | ||
| 105 | ret = iphone_mux_recv(client->connection, (char *) &datalen, sizeof(datalen), &bytes); | ||
| 106 | else { | ||
| 107 | bytes = gnutls_record_recv(*client->ssl_session, &datalen, sizeof(datalen)); | ||
| 108 | if (bytes > 0) | ||
| 109 | ret = IPHONE_E_SUCCESS; | ||
| 110 | } | ||
| 111 | datalen = ntohl(datalen); | ||
| 112 | |||
| 113 | receive = (char *) malloc(sizeof(char) * datalen); | ||
| 114 | if (!client->in_SSL) | ||
| 115 | ret = iphone_mux_recv(client->connection, receive, datalen, &bytes); | ||
| 116 | else { | ||
| 117 | bytes = gnutls_record_recv(*client->ssl_session, receive, datalen); | ||
| 118 | if (bytes > 0) | ||
| 119 | ret = IPHONE_E_SUCCESS; | ||
| 120 | } | ||
| 121 | *dump_data = receive; | ||
| 122 | *recv_bytes = bytes; | ||
| 123 | return ret; | ||
| 124 | } | ||
| 125 | |||
| 126 | /** Sends lockdownd data to the iPhone | ||
| 127 | * | ||
| 128 | * @note This function is low-level and should only be used if you need to send | ||
| 129 | * a new type of message. | ||
| 130 | * | ||
| 131 | * @param control The lockdownd client | ||
| 132 | * @param raw_data The null terminated string buffer to send | ||
| 133 | * @param length The length of data to send | ||
| 134 | * | ||
| 135 | * @return The number of bytes sent | ||
| 136 | */ | ||
| 137 | iphone_error_t iphone_lckd_send(iphone_lckd_client_t client, char *raw_data, uint32_t length, uint32_t * sent_bytes) | ||
| 138 | { | ||
| 139 | if (!client || !raw_data || length == 0 || !sent_bytes) | ||
| 140 | return IPHONE_E_INVALID_ARG; | ||
| 141 | char *real_query; | ||
| 142 | int bytes; | ||
| 143 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 144 | |||
| 145 | real_query = (char *) malloc(sizeof(char) * (length + 4)); | ||
| 146 | length = htonl(length); | ||
| 147 | memcpy(real_query, &length, sizeof(length)); | ||
| 148 | memcpy(real_query + 4, raw_data, ntohl(length)); | ||
| 149 | log_debug_msg("lockdownd_send(): made the query, sending it along\n"); | ||
| 150 | dump_debug_buffer("grpkt", real_query, ntohl(length) + 4); | ||
| 151 | |||
| 152 | if (!client->in_SSL) | ||
| 153 | ret = iphone_mux_send(client->connection, real_query, ntohl(length) + sizeof(length), &bytes); | ||
| 154 | else { | ||
| 155 | gnutls_record_send(*client->ssl_session, real_query, ntohl(length) + sizeof(length)); | ||
| 156 | ret = IPHONE_E_SUCCESS; | ||
| 157 | } | ||
| 158 | log_debug_msg("lockdownd_send(): sent it!\n"); | ||
| 159 | free(real_query); | ||
| 160 | *sent_bytes = bytes; | ||
| 161 | return ret; | ||
| 162 | } | ||
| 163 | |||
| 164 | /** Initiates the handshake for the lockdown session. Part of the lockdownd handshake. | ||
| 165 | * | ||
| 166 | * @note You most likely want lockdownd_init unless you are doing something special. | ||
| 167 | * | ||
| 168 | * @param control The lockdownd client | ||
| 169 | * | ||
| 170 | * @return 1 on success and 0 on failure. | ||
| 171 | */ | ||
| 172 | iphone_error_t lockdownd_hello(iphone_lckd_client_t control) | ||
| 173 | { | ||
| 174 | if (!control) | ||
| 175 | return IPHONE_E_INVALID_ARG; | ||
| 176 | |||
| 177 | int bytes = 0, i = 0; | ||
| 178 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 179 | |||
| 180 | plist_t dict = NULL; | ||
| 181 | plist_new_dict(&dict); | ||
| 182 | |||
| 183 | plist_add_dict_element(dict, "Request", PLIST_STRING, (void *) "QueryType", strlen("QueryType")); | ||
| 184 | |||
| 185 | log_debug_msg("lockdownd_hello() called\n"); | ||
| 186 | char *XML_content = NULL; | ||
| 187 | uint32_t length = 0; | ||
| 188 | |||
| 189 | plist_to_xml(dict, &XML_content, &length); | ||
| 190 | log_debug_msg("Send msg :\nsize : %i\nxml : %s", length, XML_content); | ||
| 191 | ret = iphone_lckd_send(control, XML_content, length, &bytes); | ||
| 192 | |||
| 193 | free(XML_content); | ||
| 194 | XML_content = NULL; | ||
| 195 | plist_free(dict); | ||
| 196 | dict = NULL; | ||
| 197 | |||
| 198 | ret = iphone_lckd_recv(control, &XML_content, &bytes); | ||
| 199 | log_debug_msg("Receive msg :\nsize : %i\nxml : %s", bytes, XML_content); | ||
| 200 | xml_to_plist(XML_content, bytes, &dict); | ||
| 201 | |||
| 202 | if (!dict) | ||
| 203 | return IPHONE_E_PLIST_ERROR; | ||
| 204 | |||
| 205 | plist_t query_node = find_query_node(dict, "Request", "QueryType"); | ||
| 206 | plist_t result_node = g_node_next_sibling(query_node); | ||
| 207 | plist_t value_node = g_node_next_sibling(result_node); | ||
| 208 | |||
| 209 | plist_type result_type; | ||
| 210 | plist_type value_type; | ||
| 211 | |||
| 212 | char *result_value = NULL; | ||
| 213 | char *value_value = NULL; | ||
| 214 | uint64_t result_length = 0; | ||
| 215 | uint64_t value_length = 0; | ||
| 216 | |||
| 217 | get_type_and_value(result_node, &result_type, (void *) (&result_value), &result_length); | ||
| 218 | get_type_and_value(value_node, &value_type, (void *) (&value_value), &value_length); | ||
| 219 | |||
| 220 | if (result_type == PLIST_KEY && | ||
| 221 | value_type == PLIST_STRING && !strcmp(result_value, "Result") && !strcmp(value_value, "Success")) { | ||
| 222 | log_debug_msg("lockdownd_hello(): success\n"); | ||
| 223 | ret = IPHONE_E_SUCCESS; | ||
| 224 | } | ||
| 225 | |||
| 226 | return ret; | ||
| 227 | } | ||
| 228 | |||
| 229 | /** Generic function to handle simple (key, value) requests. | ||
| 230 | * | ||
| 231 | * @param control an initialized lockdownd client. | ||
| 232 | * @param key the key to request | ||
| 233 | * @param value a pointer to the requested value | ||
| 234 | * | ||
| 235 | * @return IPHONE_E_SUCCESS on success. | ||
| 236 | */ | ||
| 237 | iphone_error_t lockdownd_generic_get_value(iphone_lckd_client_t control, char *req_key, char *req_string, | ||
| 238 | gnutls_datum_t * value) | ||
| 239 | { | ||
| 240 | if (!control || !req_key || !value || value->data) | ||
| 241 | return IPHONE_E_INVALID_ARG; | ||
| 242 | |||
| 243 | plist_t dict = NULL; | ||
| 244 | int bytes = 0, i = 0; | ||
| 245 | char *XML_content = NULL; | ||
| 246 | uint32_t length = 0; | ||
| 247 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 248 | |||
| 249 | /* Setup DevicePublicKey request plist */ | ||
| 250 | plist_new_dict(&dict); | ||
| 251 | plist_add_dict_element(dict, req_key, PLIST_STRING, (void *) req_string, strlen(req_string)); | ||
| 252 | plist_add_dict_element(dict, "Request", PLIST_STRING, (void *) "GetValue", strlen("GetValue")); | ||
| 253 | plist_to_xml(dict, &XML_content, &length); | ||
| 254 | |||
| 255 | /* send to iPhone */ | ||
| 256 | log_debug_msg("Send msg :\nsize : %i\nxml : %s", length, XML_content); | ||
| 257 | ret = iphone_lckd_send(control, XML_content, length, &bytes); | ||
| 258 | |||
| 259 | free(XML_content); | ||
| 260 | XML_content = NULL; | ||
| 261 | plist_free(dict); | ||
| 262 | dict = NULL; | ||
| 263 | |||
| 264 | if (ret != IPHONE_E_SUCCESS) | ||
| 265 | return ret; | ||
| 266 | |||
| 267 | /* Now get iPhone's answer */ | ||
| 268 | ret = iphone_lckd_recv(control, &XML_content, &bytes); | ||
| 269 | log_debug_msg("Receive msg :\nsize : %i\nxml : %s", bytes, XML_content); | ||
| 270 | |||
| 271 | if (ret != IPHONE_E_SUCCESS) | ||
| 272 | return ret; | ||
| 273 | |||
| 274 | xml_to_plist(XML_content, bytes, &dict); | ||
| 275 | if (!dict) | ||
| 276 | return IPHONE_E_PLIST_ERROR; | ||
| 277 | |||
| 278 | plist_t query_node = find_query_node(dict, "Request", "GetValue"); | ||
| 279 | plist_t result_key_node = g_node_next_sibling(query_node); | ||
| 280 | plist_t result_value_node = g_node_next_sibling(result_key_node); | ||
| 281 | |||
| 282 | plist_type result_key_type; | ||
| 283 | plist_type result_value_type; | ||
| 284 | char *result_key = NULL; | ||
| 285 | char *result_value = NULL; | ||
| 286 | uint64_t result_length = 0; | ||
| 287 | uint64_t value_length = 0; | ||
| 288 | |||
| 289 | get_type_and_value(result_key_node, &result_key_type, (void *) (&result_key), &result_length); | ||
| 290 | get_type_and_value(result_value_node, &result_value_type, (void *) (&result_value), &value_length); | ||
| 291 | |||
| 292 | if (result_key_type == PLIST_KEY && | ||
| 293 | result_value_type == PLIST_STRING && !strcmp(result_key, "Result") && !strcmp(result_value, "Success")) { | ||
| 294 | log_debug_msg("lockdownd_generic_get_value(): success\n"); | ||
| 295 | ret = IPHONE_E_SUCCESS; | ||
| 296 | } | ||
| 297 | |||
| 298 | if (ret != IPHONE_E_SUCCESS) { | ||
| 299 | return IPHONE_E_DICT_ERROR; | ||
| 300 | } | ||
| 301 | |||
| 302 | plist_t value_key_node = g_node_next_sibling(result_key_node); | ||
| 303 | plist_t value_value_node = g_node_next_sibling(value_key_node); | ||
| 304 | plist_type value_key_type; | ||
| 305 | plist_type value_value_type; | ||
| 306 | char *value_key = NULL; | ||
| 307 | char *value_value = NULL; | ||
| 308 | uint64_t key_length = 0; | ||
| 309 | uint64_t valval_length = 0; | ||
| 310 | |||
| 311 | get_type_and_value(value_key_node, &value_key_type, (void *) (&value_key), &key_length); | ||
| 312 | get_type_and_value(value_value_node, &value_value_type, (void *) (&value_value), &valval_length); | ||
| 313 | |||
| 314 | if (value_key_type == PLIST_KEY && !strcmp(result_key, "Value")) { | ||
| 315 | log_debug_msg("lockdownd_generic_get_value(): success\n"); | ||
| 316 | value->data = value_value; | ||
| 317 | value->size = valval_length; | ||
| 318 | ret = IPHONE_E_SUCCESS; | ||
| 319 | } | ||
| 320 | |||
| 321 | plist_free(dict); | ||
| 322 | free(XML_content); | ||
| 323 | return ret; | ||
| 324 | } | ||
| 325 | |||
| 326 | /** Askes for the device's unique id. Part of the lockdownd handshake. | ||
| 327 | * | ||
| 328 | * @note You most likely want lockdownd_init unless you are doing something special. | ||
| 329 | * | ||
| 330 | * @return 1 on success and 0 on failure. | ||
| 331 | */ | ||
| 332 | iphone_error_t lockdownd_get_device_uid(iphone_lckd_client_t control, char **uid) | ||
| 333 | { | ||
| 334 | gnutls_datum_t temp = { NULL, 0 }; | ||
| 335 | return lockdownd_generic_get_value(control, "Key", "UniqueDeviceID", &temp); | ||
| 336 | *uid = temp.data; | ||
| 337 | } | ||
| 338 | |||
| 339 | /** Askes for the device's public key. Part of the lockdownd handshake. | ||
| 340 | * | ||
| 341 | * @note You most likely want lockdownd_init unless you are doing something special. | ||
| 342 | * | ||
| 343 | * @return 1 on success and 0 on failure. | ||
| 344 | */ | ||
| 345 | iphone_error_t lockdownd_get_device_public_key(iphone_lckd_client_t control, gnutls_datum_t * public_key) | ||
| 346 | { | ||
| 347 | return lockdownd_generic_get_value(control, "Key", "DevicePublicKey", public_key); | ||
| 348 | } | ||
| 349 | |||
| 350 | /** Completes the entire lockdownd handshake. | ||
| 351 | * | ||
| 352 | * @param phone The iPhone | ||
| 353 | * @param lockdownd_client The pointer to the location of the lockdownd_client | ||
| 354 | * | ||
| 355 | * @return 1 on success and 0 on failure | ||
| 356 | */ | ||
| 357 | iphone_error_t iphone_lckd_new_client(iphone_device_t device, iphone_lckd_client_t * client) | ||
| 358 | { | ||
| 359 | if (!device || !client || (client && *client)) | ||
| 360 | return IPHONE_E_INVALID_ARG; | ||
| 361 | iphone_error_t ret = IPHONE_E_SUCCESS; | ||
| 362 | char *host_id = NULL; | ||
| 363 | |||
| 364 | iphone_lckd_client_t client_loc = new_lockdownd_client(device); | ||
| 365 | if (IPHONE_E_SUCCESS != lockdownd_hello(client_loc)) { | ||
| 366 | fprintf(stderr, "Hello failed in the lockdownd client.\n"); | ||
| 367 | ret = IPHONE_E_NOT_ENOUGH_DATA; | ||
| 368 | } | ||
| 369 | |||
| 370 | |||
| 371 | char *uid = NULL; | ||
| 372 | ret = lockdownd_get_device_uid(client_loc, &uid); | ||
| 373 | if (IPHONE_E_SUCCESS != ret) { | ||
| 374 | fprintf(stderr, "Device refused to send uid.\n"); | ||
| 375 | } | ||
| 376 | |||
| 377 | host_id = get_host_id(); | ||
| 378 | if (IPHONE_E_SUCCESS == ret && !host_id) { | ||
| 379 | fprintf(stderr, "No HostID found, run libiphone-initconf.\n"); | ||
| 380 | ret = IPHONE_E_INVALID_CONF; | ||
| 381 | } | ||
| 382 | |||
| 383 | if (IPHONE_E_SUCCESS == ret && !is_device_known(uid)) | ||
| 384 | ret = lockdownd_pair_device(client_loc, uid, host_id); | ||
| 385 | |||
| 386 | if (uid) { | ||
| 387 | free(uid); | ||
| 388 | uid = NULL; | ||
| 389 | } | ||
| 390 | |||
| 391 | ret = lockdownd_start_SSL_session(client_loc, host_id); | ||
| 392 | if (IPHONE_E_SUCCESS != ret) { | ||
| 393 | ret = IPHONE_E_SSL_ERROR; | ||
| 394 | fprintf(stderr, "SSL Session opening failed.\n"); | ||
| 395 | } | ||
| 396 | |||
| 397 | if (host_id) { | ||
| 398 | free(host_id); | ||
| 399 | host_id = NULL; | ||
| 400 | } | ||
| 401 | |||
| 402 | if (IPHONE_E_SUCCESS == ret) | ||
| 403 | *client = client_loc; | ||
| 404 | return ret; | ||
| 405 | } | ||
| 406 | |||
| 407 | /** Generates the appropriate keys and pairs the device. It's part of the | ||
| 408 | * lockdownd handshake. | ||
| 409 | * | ||
| 410 | * @note You most likely want lockdownd_init unless you are doing something special. | ||
| 411 | * | ||
| 412 | * @return 1 on success and 0 on failure | ||
| 413 | */ | ||
| 414 | iphone_error_t lockdownd_pair_device(iphone_lckd_client_t control, char *uid, char *host_id) | ||
| 415 | { | ||
| 416 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 417 | plist_t dict = NULL; | ||
| 418 | plist_t dict_record = NULL; | ||
| 419 | int bytes = 0, i = 0; | ||
| 420 | char *XML_content = NULL; | ||
| 421 | uint32_t length = 0; | ||
| 422 | |||
| 423 | gnutls_datum_t device_cert = { NULL, 0 }; | ||
| 424 | gnutls_datum_t host_cert = { NULL, 0 }; | ||
| 425 | gnutls_datum_t root_cert = { NULL, 0 }; | ||
| 426 | gnutls_datum_t public_key = { NULL, 0 }; | ||
| 427 | |||
| 428 | ret = lockdownd_get_device_public_key(control, &public_key); | ||
| 429 | if (ret != IPHONE_E_SUCCESS) { | ||
| 430 | fprintf(stderr, "Device refused to send public key.\n"); | ||
| 431 | return ret; | ||
| 432 | } | ||
| 433 | |||
| 434 | ret = lockdownd_gen_pair_cert(public_key, &device_cert, &host_cert, &root_cert); | ||
| 435 | if (ret != IPHONE_E_SUCCESS) { | ||
| 436 | free(public_key.data); | ||
| 437 | return ret; | ||
| 438 | } | ||
| 439 | |||
| 440 | /* Setup Pair request plist */ | ||
| 441 | plist_new_dict(&dict); | ||
| 442 | plist_add_dict_element(dict, "PairRecord", PLIST_DICT, NULL, 0); | ||
| 443 | dict_record = g_node_last_child(dict); | ||
| 444 | plist_add_dict_element(dict_record, "DeviceCertificate", PLIST_DATA, (void *) device_cert.data, device_cert.size); | ||
| 445 | plist_add_dict_element(dict_record, "HostCertificate", PLIST_DATA, (void *) host_cert.data, host_cert.size); | ||
| 446 | plist_add_dict_element(dict_record, "HostID", PLIST_STRING, (void *) host_id, strlen(host_id)); | ||
| 447 | plist_add_dict_element(dict_record, "RootCertificate", PLIST_DATA, (void *) root_cert.data, root_cert.size); | ||
| 448 | plist_add_dict_element(dict, "Request", PLIST_STRING, (void *) "Pair", strlen("Pair")); | ||
| 449 | plist_to_xml(dict, &XML_content, &length); | ||
| 450 | log_debug_msg("XML Pairing request :\nsize : %i\nxml :\n %s", length, XML_content); | ||
| 451 | |||
| 452 | /* send to iPhone */ | ||
| 453 | ret = iphone_lckd_send(control, XML_content, length, &bytes); | ||
| 454 | |||
| 455 | free(XML_content); | ||
| 456 | plist_free(dict); | ||
| 457 | dict = NULL; | ||
| 458 | |||
| 459 | if (ret != IPHONE_E_SUCCESS) | ||
| 460 | return ret; | ||
| 461 | |||
| 462 | /* Now get iPhone's answer */ | ||
| 463 | ret = iphone_lckd_recv(control, &XML_content, &bytes); | ||
| 464 | |||
| 465 | if (ret != IPHONE_E_SUCCESS) | ||
| 466 | return ret; | ||
| 467 | |||
| 468 | log_debug_msg("lockdown_pair_device: iPhone's response to our pair request:\n"); | ||
| 469 | log_debug_msg(XML_content); | ||
| 470 | log_debug_msg("\n\n"); | ||
| 471 | |||
| 472 | xml_to_plist(XML_content, bytes, &dict); | ||
| 473 | if (!dict) | ||
| 474 | return IPHONE_E_PLIST_ERROR; | ||
| 475 | |||
| 476 | plist_t query_node = find_query_node(dict, "Request", "Pair"); | ||
| 477 | plist_t result_key_node = g_node_next_sibling(query_node); | ||
| 478 | plist_t result_value_node = g_node_next_sibling(result_key_node); | ||
| 479 | |||
| 480 | plist_type result_key_type; | ||
| 481 | plist_type result_value_type; | ||
| 482 | char *result_key = NULL; | ||
| 483 | char *result_value = NULL; | ||
| 484 | uint64_t key_length = 0; | ||
| 485 | uint64_t val_length = 0; | ||
| 486 | |||
| 487 | get_type_and_value(result_key_node, &result_key_type, (void *) (&result_key), &key_length); | ||
| 488 | get_type_and_value(result_value_node, &result_value_type, (void *) (&result_value), &val_length); | ||
| 489 | |||
| 490 | if (result_key_type == PLIST_KEY && | ||
| 491 | result_value_type == PLIST_STRING && !strcmp(result_key, "Result") && !strcmp(result_value, "Success")) { | ||
| 492 | ret = IPHONE_E_SUCCESS; | ||
| 493 | } | ||
| 494 | |||
| 495 | /* store public key in config if pairing succeeded */ | ||
| 496 | if (ret == IPHONE_E_SUCCESS) { | ||
| 497 | log_debug_msg("lockdownd_pair_device: pair success\n"); | ||
| 498 | store_device_public_key(uid, public_key); | ||
| 499 | ret = IPHONE_E_SUCCESS; | ||
| 500 | } else { | ||
| 501 | log_debug_msg("lockdownd_pair_device: pair failure\n"); | ||
| 502 | ret = IPHONE_E_PAIRING_FAILED; | ||
| 503 | } | ||
| 504 | free(public_key.data); | ||
| 505 | return ret; | ||
| 506 | } | ||
| 507 | |||
| 508 | /** Generates the device certificate from the public key as well as the host | ||
| 509 | * and root certificates. | ||
| 510 | * | ||
| 511 | * @return IPHONE_E_SUCCESS on success. | ||
| 512 | */ | ||
| 513 | iphone_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * odevice_cert, | ||
| 514 | gnutls_datum_t * ohost_cert, gnutls_datum_t * oroot_cert) | ||
| 515 | { | ||
| 516 | if (!public_key.data || !odevice_cert || !ohost_cert || !oroot_cert) | ||
| 517 | return IPHONE_E_INVALID_ARG; | ||
| 518 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 519 | |||
| 520 | gnutls_datum_t modulus = { NULL, 0 }; | ||
| 521 | gnutls_datum_t exponent = { NULL, 0 }; | ||
| 522 | |||
| 523 | /* now decode the PEM encoded key */ | ||
| 524 | gnutls_datum_t der_pub_key; | ||
| 525 | if (GNUTLS_E_SUCCESS == gnutls_pem_base64_decode_alloc("RSA PUBLIC KEY", &public_key, &der_pub_key)) { | ||
| 526 | |||
| 527 | /* initalize asn.1 parser */ | ||
| 528 | ASN1_TYPE pkcs1 = ASN1_TYPE_EMPTY; | ||
| 529 | if (ASN1_SUCCESS == asn1_array2tree(pkcs1_asn1_tab, &pkcs1, NULL)) { | ||
| 530 | |||
| 531 | ASN1_TYPE asn1_pub_key = ASN1_TYPE_EMPTY; | ||
| 532 | asn1_create_element(pkcs1, "PKCS1.RSAPublicKey", &asn1_pub_key); | ||
| 533 | |||
| 534 | if (ASN1_SUCCESS == asn1_der_decoding(&asn1_pub_key, der_pub_key.data, der_pub_key.size, NULL)) { | ||
| 535 | |||
| 536 | /* get size to read */ | ||
| 537 | int ret1 = asn1_read_value(asn1_pub_key, "modulus", NULL, &modulus.size); | ||
| 538 | int ret2 = asn1_read_value(asn1_pub_key, "publicExponent", NULL, &exponent.size); | ||
| 539 | |||
| 540 | modulus.data = gnutls_malloc(modulus.size); | ||
| 541 | exponent.data = gnutls_malloc(exponent.size); | ||
| 542 | |||
| 543 | ret1 = asn1_read_value(asn1_pub_key, "modulus", modulus.data, &modulus.size); | ||
| 544 | ret2 = asn1_read_value(asn1_pub_key, "publicExponent", exponent.data, &exponent.size); | ||
| 545 | if (ASN1_SUCCESS == ret1 && ASN1_SUCCESS == ret2) | ||
| 546 | ret = IPHONE_E_SUCCESS; | ||
| 547 | } | ||
| 548 | if (asn1_pub_key) | ||
| 549 | asn1_delete_structure(&asn1_pub_key); | ||
| 550 | } | ||
| 551 | if (pkcs1) | ||
| 552 | asn1_delete_structure(&pkcs1); | ||
| 553 | } | ||
| 554 | |||
| 555 | /* now generate certifcates */ | ||
| 556 | if (IPHONE_E_SUCCESS == ret && 0 != modulus.size && 0 != exponent.size) { | ||
| 557 | |||
| 558 | gnutls_global_init(); | ||
| 559 | gnutls_datum_t essentially_null = { strdup("abababababababab"), strlen("abababababababab") }; | ||
| 560 | |||
| 561 | gnutls_x509_privkey_t fake_privkey, root_privkey; | ||
| 562 | gnutls_x509_crt_t dev_cert, root_cert, host_cert; | ||
| 563 | |||
| 564 | gnutls_x509_privkey_init(&fake_privkey); | ||
| 565 | gnutls_x509_crt_init(&dev_cert); | ||
| 566 | gnutls_x509_crt_init(&root_cert); | ||
| 567 | gnutls_x509_crt_init(&host_cert); | ||
| 568 | |||
| 569 | if (GNUTLS_E_SUCCESS == | ||
| 570 | gnutls_x509_privkey_import_rsa_raw(fake_privkey, &modulus, &exponent, &essentially_null, &essentially_null, | ||
| 571 | &essentially_null, &essentially_null)) { | ||
| 572 | |||
| 573 | gnutls_x509_privkey_init(&root_privkey); | ||
| 574 | |||
| 575 | /* get root cert */ | ||
| 576 | gnutls_datum_t pem_root_cert = { NULL, 0 }; | ||
| 577 | get_root_certificate(&pem_root_cert); | ||
| 578 | if (GNUTLS_E_SUCCESS != gnutls_x509_crt_import(root_cert, &pem_root_cert, GNUTLS_X509_FMT_PEM)) | ||
| 579 | ret = IPHONE_E_SSL_ERROR; | ||
| 580 | |||
| 581 | /* get host cert */ | ||
| 582 | gnutls_datum_t pem_host_cert = { NULL, 0 }; | ||
| 583 | get_host_certificate(&pem_host_cert); | ||
| 584 | if (GNUTLS_E_SUCCESS != gnutls_x509_crt_import(host_cert, &pem_host_cert, GNUTLS_X509_FMT_PEM)) | ||
| 585 | ret = IPHONE_E_SSL_ERROR; | ||
| 586 | |||
| 587 | /* get root private key */ | ||
| 588 | gnutls_datum_t pem_root_priv = { NULL, 0 }; | ||
| 589 | get_root_private_key(&pem_root_priv); | ||
| 590 | if (GNUTLS_E_SUCCESS != gnutls_x509_privkey_import(root_privkey, &pem_root_priv, GNUTLS_X509_FMT_PEM)) | ||
| 591 | ret = IPHONE_E_SSL_ERROR; | ||
| 592 | |||
| 593 | /* generate device certificate */ | ||
| 594 | gnutls_x509_crt_set_key(dev_cert, fake_privkey); | ||
| 595 | gnutls_x509_crt_set_serial(dev_cert, "\x00", 1); | ||
| 596 | gnutls_x509_crt_set_version(dev_cert, 3); | ||
| 597 | gnutls_x509_crt_set_ca_status(dev_cert, 0); | ||
| 598 | gnutls_x509_crt_set_activation_time(dev_cert, time(NULL)); | ||
| 599 | gnutls_x509_crt_set_expiration_time(dev_cert, time(NULL) + (60 * 60 * 24 * 365 * 10)); | ||
| 600 | gnutls_x509_crt_sign(dev_cert, root_cert, root_privkey); | ||
| 601 | |||
| 602 | if (IPHONE_E_SUCCESS == ret) { | ||
| 603 | /* if everything went well, export in PEM format */ | ||
| 604 | gnutls_datum_t dev_pem = { NULL, 0 }; | ||
| 605 | gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, NULL, &dev_pem.size); | ||
| 606 | dev_pem.data = gnutls_malloc(dev_pem.size); | ||
| 607 | gnutls_x509_crt_export(dev_cert, GNUTLS_X509_FMT_PEM, dev_pem.data, &dev_pem.size); | ||
| 608 | |||
| 609 | /* copy buffer for output */ | ||
| 610 | odevice_cert->data = malloc(dev_pem.size); | ||
| 611 | memcpy(odevice_cert->data, dev_pem.data, dev_pem.size); | ||
| 612 | odevice_cert->size = dev_pem.size; | ||
| 613 | |||
| 614 | ohost_cert->data = malloc(pem_host_cert.size); | ||
| 615 | memcpy(ohost_cert->data, pem_host_cert.data, pem_host_cert.size); | ||
| 616 | ohost_cert->size = pem_host_cert.size; | ||
| 617 | |||
| 618 | oroot_cert->data = malloc(pem_root_cert.size); | ||
| 619 | memcpy(oroot_cert->data, pem_root_cert.data, pem_root_cert.size); | ||
| 620 | oroot_cert->size = pem_root_cert.size; | ||
| 621 | } | ||
| 622 | gnutls_free(pem_root_priv.data); | ||
| 623 | gnutls_free(pem_root_cert.data); | ||
| 624 | gnutls_free(pem_host_cert.data); | ||
| 625 | } | ||
| 626 | } | ||
| 627 | |||
| 628 | gnutls_free(modulus.data); | ||
| 629 | gnutls_free(exponent.data); | ||
| 630 | |||
| 631 | gnutls_free(der_pub_key.data); | ||
| 632 | |||
| 633 | return ret; | ||
| 634 | } | ||
| 635 | |||
| 636 | /** Starts SSL communication with lockdownd after the iPhone has been paired. | ||
| 637 | * | ||
| 638 | * @param control The lockdownd client | ||
| 639 | * @param HostID The HostID used with this phone | ||
| 640 | * | ||
| 641 | * @return 1 on success and 0 on failure | ||
| 642 | */ | ||
| 643 | iphone_error_t lockdownd_start_SSL_session(iphone_lckd_client_t control, const char *HostID) | ||
| 644 | { | ||
| 645 | plist_t dict = NULL; | ||
| 646 | char *XML_content = NULL; | ||
| 647 | uint32_t length = 0, bytes = 0, return_me = 0; | ||
| 648 | |||
| 649 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 650 | |||
| 651 | /* Setup DevicePublicKey request plist */ | ||
| 652 | plist_new_dict(&dict); | ||
| 653 | plist_add_dict_element(dict, "HostID", PLIST_STRING, (void *) HostID, strlen(HostID)); | ||
| 654 | plist_add_dict_element(dict, "Request", PLIST_STRING, (void *) "StartSession", strlen("StartSession")); | ||
| 655 | plist_to_xml(dict, &XML_content, &length); | ||
| 656 | log_debug_msg("Send msg :\nsize : %i\nxml : %s", length, XML_content); | ||
| 657 | |||
| 658 | ret = iphone_lckd_send(control, XML_content, length, &bytes); | ||
| 659 | |||
| 660 | free(XML_content); | ||
| 661 | XML_content = NULL; | ||
| 662 | plist_free(dict); | ||
| 663 | dict = NULL; | ||
| 664 | |||
| 665 | if (ret != IPHONE_E_SUCCESS) | ||
| 666 | return ret; | ||
| 667 | |||
| 668 | if (bytes > 0) { | ||
| 669 | ret = iphone_lckd_recv(control, &XML_content, &bytes); | ||
| 670 | log_debug_msg("Receive msg :\nsize : %i\nxml : %s", bytes, XML_content); | ||
| 671 | xml_to_plist(XML_content, bytes, &dict); | ||
| 672 | if (!dict) | ||
| 673 | return IPHONE_E_PLIST_ERROR; | ||
| 674 | |||
| 675 | plist_t query_node = find_query_node(dict, "Request", "StartSession"); | ||
| 676 | plist_t result_key_node = g_node_next_sibling(query_node); | ||
| 677 | plist_t result_value_node = g_node_next_sibling(result_key_node); | ||
| 678 | |||
| 679 | plist_type result_key_type; | ||
| 680 | plist_type result_value_type; | ||
| 681 | char *result_key = NULL; | ||
| 682 | char *result_value = NULL; | ||
| 683 | uint64_t key_length = 0; | ||
| 684 | uint64_t val_length = 0; | ||
| 685 | |||
| 686 | get_type_and_value(result_key_node, &result_key_type, (void *) (&result_key), &key_length); | ||
| 687 | get_type_and_value(result_value_node, &result_value_type, (void *) (&result_value), &val_length); | ||
| 688 | |||
| 689 | free(XML_content); | ||
| 690 | XML_content = NULL; | ||
| 691 | plist_free(dict); | ||
| 692 | dict = NULL; | ||
| 693 | |||
| 694 | if (result_key_type == PLIST_KEY && | ||
| 695 | result_value_type == PLIST_STRING && !strcmp(result_key, "Result") && !strcmp(result_value, "Success")) { | ||
| 696 | // Set up GnuTLS... | ||
| 697 | //gnutls_anon_client_credentials_t anoncred; | ||
| 698 | gnutls_certificate_credentials_t xcred; | ||
| 699 | |||
| 700 | log_debug_msg("We started the session OK, now trying GnuTLS\n"); | ||
| 701 | errno = 0; | ||
| 702 | gnutls_global_init(); | ||
| 703 | //gnutls_anon_allocate_client_credentials(&anoncred); | ||
| 704 | gnutls_certificate_allocate_credentials(&xcred); | ||
| 705 | gnutls_certificate_set_x509_trust_file(xcred, "hostcert.pem", GNUTLS_X509_FMT_PEM); | ||
| 706 | gnutls_init(control->ssl_session, GNUTLS_CLIENT); | ||
| 707 | { | ||
| 708 | int protocol_priority[16] = { GNUTLS_SSL3, 0 }; | ||
| 709 | int kx_priority[16] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA, 0 }; | ||
| 710 | int cipher_priority[16] = { GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; | ||
| 711 | int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 }; | ||
| 712 | int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; | ||
| 713 | |||
| 714 | gnutls_cipher_set_priority(*control->ssl_session, cipher_priority); | ||
| 715 | gnutls_compression_set_priority(*control->ssl_session, comp_priority); | ||
| 716 | gnutls_kx_set_priority(*control->ssl_session, kx_priority); | ||
| 717 | gnutls_protocol_set_priority(*control->ssl_session, protocol_priority); | ||
| 718 | gnutls_mac_set_priority(*control->ssl_session, mac_priority); | ||
| 719 | |||
| 720 | } | ||
| 721 | gnutls_credentials_set(*control->ssl_session, GNUTLS_CRD_CERTIFICATE, xcred); // this part is killing me. | ||
| 722 | |||
| 723 | log_debug_msg("GnuTLS step 1...\n"); | ||
| 724 | gnutls_transport_set_ptr(*control->ssl_session, (gnutls_transport_ptr_t) control); | ||
| 725 | log_debug_msg("GnuTLS step 2...\n"); | ||
| 726 | gnutls_transport_set_push_function(*control->ssl_session, (gnutls_push_func) & lockdownd_secuwrite); | ||
| 727 | log_debug_msg("GnuTLS step 3...\n"); | ||
| 728 | gnutls_transport_set_pull_function(*control->ssl_session, (gnutls_pull_func) & lockdownd_securead); | ||
| 729 | log_debug_msg("GnuTLS step 4 -- now handshaking...\n"); | ||
| 730 | |||
| 731 | if (errno) | ||
| 732 | log_debug_msg("WARN: errno says %s before handshake!\n", strerror(errno)); | ||
| 733 | return_me = gnutls_handshake(*control->ssl_session); | ||
| 734 | log_debug_msg("GnuTLS handshake done...\n"); | ||
| 735 | |||
| 736 | if (return_me != GNUTLS_E_SUCCESS) { | ||
| 737 | log_debug_msg("GnuTLS reported something wrong.\n"); | ||
| 738 | gnutls_perror(return_me); | ||
| 739 | log_debug_msg("oh.. errno says %s\n", strerror(errno)); | ||
| 740 | return IPHONE_E_SSL_ERROR; | ||
| 741 | } else { | ||
| 742 | control->in_SSL = 1; | ||
| 743 | return IPHONE_E_SUCCESS; | ||
| 744 | } | ||
| 745 | } | ||
| 746 | |||
| 747 | log_debug_msg("Apparently failed negotiating with lockdownd.\n"); | ||
| 748 | log_debug_msg("Responding dictionary: \n"); | ||
| 749 | return IPHONE_E_SSL_ERROR; | ||
| 750 | } else { | ||
| 751 | log_debug_msg("Didn't get enough bytes.\n"); | ||
| 752 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 753 | } | ||
| 754 | } | ||
| 755 | |||
| 756 | /** gnutls callback for writing data to the iPhone. | ||
| 757 | * | ||
| 758 | * @param transport It's really the lockdownd client, but the method signature has to match | ||
| 759 | * @param buffer The data to send | ||
| 760 | * @param length The length of data to send in bytes | ||
| 761 | * | ||
| 762 | * @return The number of bytes sent | ||
| 763 | */ | ||
| 764 | ssize_t lockdownd_secuwrite(gnutls_transport_ptr_t transport, char *buffer, size_t length) | ||
| 765 | { | ||
| 766 | int bytes = 0; | ||
| 767 | iphone_lckd_client_t control; | ||
| 768 | control = (iphone_lckd_client_t) transport; | ||
| 769 | log_debug_msg("lockdownd_secuwrite() called\n"); | ||
| 770 | log_debug_msg("pre-send\nlength = %zi\n", length); | ||
| 771 | iphone_mux_send(control->connection, buffer, length, &bytes); | ||
| 772 | log_debug_msg("post-send\nsent %i bytes\n", bytes); | ||
| 773 | |||
| 774 | dump_debug_buffer("sslpacketwrite.out", buffer, length); | ||
| 775 | return bytes; | ||
| 776 | } | ||
| 777 | |||
| 778 | /** gnutls callback for reading data from the iPhone | ||
| 779 | * | ||
| 780 | * @param transport It's really the lockdownd client, but the method signature has to match | ||
| 781 | * @param buffer The buffer to store data in | ||
| 782 | * @param length The length of data to read in bytes | ||
| 783 | * | ||
| 784 | * @return The number of bytes read | ||
| 785 | */ | ||
| 786 | ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_t length) | ||
| 787 | { | ||
| 788 | int bytes = 0, pos_start_fill = 0; | ||
| 789 | char *hackhackhack = NULL; | ||
| 790 | iphone_lckd_client_t control; | ||
| 791 | control = (iphone_lckd_client_t) transport; | ||
| 792 | log_debug_msg("lockdownd_securead() called\nlength = %zi\n", length); | ||
| 793 | // Buffering hack! Throw what we've got in our "buffer" into the stream first, then get more. | ||
| 794 | if (control->gtls_buffer_hack_len > 0) { | ||
| 795 | if (length > control->gtls_buffer_hack_len) { // If it's asking for more than we got | ||
| 796 | length -= control->gtls_buffer_hack_len; // Subtract what we have from their requested length | ||
| 797 | pos_start_fill = control->gtls_buffer_hack_len; // set the pos to start filling at | ||
| 798 | memcpy(buffer, control->gtls_buffer_hack, control->gtls_buffer_hack_len); // Fill their buffer partially | ||
| 799 | free(control->gtls_buffer_hack); // free our memory, it's not chained anymore | ||
| 800 | control->gtls_buffer_hack_len = 0; // we don't have a hack buffer anymore | ||
| 801 | log_debug_msg("Did a partial fill to help quench thirst for data\n"); | ||
| 802 | } else if (length < control->gtls_buffer_hack_len) { // If it's asking for less... | ||
| 803 | control->gtls_buffer_hack_len -= length; // subtract what they're asking for | ||
| 804 | memcpy(buffer, control->gtls_buffer_hack, length); // fill their buffer | ||
| 805 | hackhackhack = (char *) malloc(sizeof(char) * control->gtls_buffer_hack_len); // strndup is NOT a good solution -- concatenates \0!!!! Anyway, make a new "hack" buffer. | ||
| 806 | memcpy(hackhackhack, control->gtls_buffer_hack + length, control->gtls_buffer_hack_len); // Move what's left into the new one | ||
| 807 | free(control->gtls_buffer_hack); // Free the old one | ||
| 808 | control->gtls_buffer_hack = hackhackhack; // And make it the new one. | ||
| 809 | hackhackhack = NULL; | ||
| 810 | log_debug_msg("Quenched the thirst for data; new hack length is %i\n", control->gtls_buffer_hack_len); | ||
| 811 | return length; // hand it over. | ||
| 812 | } else { // length == hack length | ||
| 813 | memcpy(buffer, control->gtls_buffer_hack, length); // copy our buffer into theirs | ||
| 814 | free(control->gtls_buffer_hack); // free our "obligation" | ||
| 815 | control->gtls_buffer_hack_len = 0; // free our "obligation" | ||
| 816 | log_debug_msg("Satiated the thirst for data; now we have to eventually receive again.\n"); | ||
| 817 | return length; // hand it over | ||
| 818 | } | ||
| 819 | } | ||
| 820 | // End buffering hack! | ||
| 821 | char *recv_buffer = (char *) malloc(sizeof(char) * (length * 1000)); // ensuring nothing stupid happens | ||
| 822 | |||
| 823 | log_debug_msg("pre-read\nclient wants %zi bytes\n", length); | ||
| 824 | iphone_mux_recv(control->connection, recv_buffer, (length * 1000), &bytes); | ||
| 825 | log_debug_msg("post-read\nwe got %i bytes\n", bytes); | ||
| 826 | if (bytes < 0) { | ||
| 827 | log_debug_msg("lockdownd_securead(): uh oh\n"); | ||
| 828 | log_debug_msg | ||
| 829 | ("I believe what we have here is a failure to communicate... libusb says %s but strerror says %s\n", | ||
| 830 | usb_strerror(), strerror(errno)); | ||
| 831 | return bytes + 28; // an errno | ||
| 832 | } | ||
| 833 | if (bytes >= length) { | ||
| 834 | if (bytes > length) { | ||
| 835 | log_debug_msg | ||
| 836 | ("lockdownd_securead: Client deliberately read less data than was there; resorting to GnuTLS buffering hack.\n"); | ||
| 837 | if (!control->gtls_buffer_hack_len) { // if there's no hack buffer yet | ||
| 838 | //control->gtls_buffer_hack = strndup(recv_buffer+length, bytes-length); // strndup is NOT a good solution! | ||
| 839 | control->gtls_buffer_hack_len += bytes - length; | ||
| 840 | control->gtls_buffer_hack = (char *) malloc(sizeof(char) * control->gtls_buffer_hack_len); | ||
| 841 | memcpy(control->gtls_buffer_hack, recv_buffer + length, control->gtls_buffer_hack_len); | ||
| 842 | } else { // if there is. | ||
| 843 | control->gtls_buffer_hack = | ||
| 844 | realloc(control->gtls_buffer_hack, control->gtls_buffer_hack_len + (bytes - length)); | ||
| 845 | memcpy(control->gtls_buffer_hack + control->gtls_buffer_hack_len, recv_buffer + length, bytes - length); | ||
| 846 | control->gtls_buffer_hack_len += bytes - length; | ||
| 847 | } | ||
| 848 | } | ||
| 849 | memcpy(buffer + pos_start_fill, recv_buffer, length); | ||
| 850 | free(recv_buffer); | ||
| 851 | if (bytes == length) { | ||
| 852 | log_debug_msg("Returning how much we received.\n"); | ||
| 853 | return bytes; | ||
| 854 | } else { | ||
| 855 | log_debug_msg("Returning what they want to hear.\nHack length: %i\n", control->gtls_buffer_hack_len); | ||
| 856 | return length; | ||
| 857 | } | ||
| 858 | } | ||
| 859 | return bytes; | ||
| 860 | } | ||
| 861 | |||
| 862 | /** Command to start the desired service | ||
| 863 | * | ||
| 864 | * @param control The lockdownd client | ||
| 865 | * @param service The name of the service to start | ||
| 866 | * | ||
| 867 | * @return The port number the service was started on or 0 on failure. | ||
| 868 | */ | ||
| 869 | iphone_error_t iphone_lckd_start_service(iphone_lckd_client_t client, const char *service, int *port) | ||
| 870 | { | ||
| 871 | if (!client || !service || !port) | ||
| 872 | return IPHONE_E_INVALID_ARG; | ||
| 873 | |||
| 874 | char *host_id = get_host_id(); | ||
| 875 | if (!host_id) | ||
| 876 | return IPHONE_E_INVALID_CONF; | ||
| 877 | if (!client->in_SSL && !lockdownd_start_SSL_session(client, host_id)) | ||
| 878 | return IPHONE_E_SSL_ERROR; | ||
| 879 | |||
| 880 | |||
| 881 | plist_t dict = NULL; | ||
| 882 | char *XML_content = NULL; | ||
| 883 | uint32_t length, i = 0, port_loc = 0, bytes = 0; | ||
| 884 | iphone_error_t ret = IPHONE_E_UNKNOWN_ERROR; | ||
| 885 | |||
| 886 | free(host_id); | ||
| 887 | host_id = NULL; | ||
| 888 | |||
| 889 | plist_new_dict(&dict); | ||
| 890 | plist_add_dict_element(dict, "Request", PLIST_STRING, (void *) "StartService", strlen("StartService")); | ||
| 891 | plist_add_dict_element(dict, "Service", PLIST_STRING, (void *) service, strlen(service)); | ||
| 892 | plist_to_xml(dict, &XML_content, &length); | ||
| 893 | |||
| 894 | /* send to iPhone */ | ||
| 895 | log_debug_msg("Send msg :\nsize : %i\nxml : %s", length, XML_content); | ||
| 896 | ret = iphone_lckd_send(client, XML_content, length, &bytes); | ||
| 897 | |||
| 898 | free(XML_content); | ||
| 899 | XML_content = NULL; | ||
| 900 | plist_free(dict); | ||
| 901 | dict = NULL; | ||
| 902 | |||
| 903 | if (IPHONE_E_SUCCESS != ret) | ||
| 904 | return ret; | ||
| 905 | |||
| 906 | ret = iphone_lckd_recv(client, &XML_content, &bytes); | ||
| 907 | |||
| 908 | if (IPHONE_E_SUCCESS != ret) | ||
| 909 | return ret; | ||
| 910 | |||
| 911 | xml_to_plist(XML_content, bytes, &dict); | ||
| 912 | if (!dict) | ||
| 913 | return IPHONE_E_PLIST_ERROR; | ||
| 914 | |||
| 915 | |||
| 916 | if (bytes <= 0) | ||
| 917 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 918 | else { | ||
| 919 | |||
| 920 | plist_t query_node = find_query_node(dict, "Request", "StartService"); | ||
| 921 | plist_t result_key_node = g_node_next_sibling(query_node); | ||
| 922 | plist_t result_value_node = g_node_next_sibling(result_key_node); | ||
| 923 | |||
| 924 | plist_t port_key_node = find_node(dict, PLIST_KEY, "Port"); | ||
| 925 | plist_t port_value_node = g_node_next_sibling(port_key_node); | ||
| 926 | |||
| 927 | plist_type result_key_type; | ||
| 928 | plist_type result_value_type; | ||
| 929 | plist_type port_key_type; | ||
| 930 | plist_type port_value_type; | ||
| 931 | char *result_key = NULL; | ||
| 932 | char *result_value = NULL; | ||
| 933 | char *port_key = NULL; | ||
| 934 | uint64_t res_key_length = 0; | ||
| 935 | uint64_t res_val_length = 0; | ||
| 936 | uint64_t port_key_length = 0; | ||
| 937 | uint64_t port_val_length = 0; | ||
| 938 | uint64_t port_value = 0; | ||
| 939 | |||
| 940 | get_type_and_value(result_key_node, &result_key_type, (void *) (&result_key), &res_key_length); | ||
| 941 | get_type_and_value(result_value_node, &result_value_type, (void *) (&result_value), &res_val_length); | ||
| 942 | get_type_and_value(port_key_node, &port_key_type, (void *) (&port_key), &port_key_length); | ||
| 943 | get_type_and_value(port_value_node, &port_value_type, (void *) (&port_value), &port_val_length); | ||
| 944 | |||
| 945 | if (result_key_type == PLIST_KEY && | ||
| 946 | result_value_type == PLIST_STRING && | ||
| 947 | port_key_type == PLIST_KEY && | ||
| 948 | port_value_type == PLIST_UINT && | ||
| 949 | !strcmp(result_key, "Result") && !strcmp(result_value, "Success") && !strcmp(port_key, "Port")) { | ||
| 950 | port_loc = port_value; | ||
| 951 | ret = IPHONE_E_SUCCESS; | ||
| 952 | } | ||
| 953 | |||
| 954 | log_debug_msg("lockdownd_start_service(): DATA RECEIVED:\n\n"); | ||
| 955 | log_debug_msg(XML_content); | ||
| 956 | log_debug_msg("end data received by lockdownd_start_service()\n"); | ||
| 957 | |||
| 958 | free(XML_content); | ||
| 959 | plist_free(dict); | ||
| 960 | dict = NULL; | ||
| 961 | if (port && ret == IPHONE_E_SUCCESS) { | ||
| 962 | *port = port_loc; | ||
| 963 | return IPHONE_E_SUCCESS; | ||
| 964 | } else | ||
| 965 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 966 | } | ||
| 967 | |||
| 968 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 969 | } | ||
diff --git a/src/lockdown.h b/src/lockdown.h deleted file mode 100644 index 8b3dd41..0000000 --- a/src/lockdown.h +++ /dev/null | |||
| @@ -1,62 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * lockdown.h | ||
| 3 | * Defines lockdown stuff, like the client struct. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #ifndef LOCKDOWND_H | ||
| 23 | #define LOCKDOWND_H | ||
| 24 | |||
| 25 | #include "usbmux.h" | ||
| 26 | #include "plist.h" | ||
| 27 | |||
| 28 | #include <gnutls/gnutls.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <libiphone/libiphone.h> | ||
| 31 | |||
| 32 | |||
| 33 | |||
| 34 | |||
| 35 | struct iphone_lckd_client_int { | ||
| 36 | iphone_umux_client_t connection; | ||
| 37 | gnutls_session_t *ssl_session; | ||
| 38 | int in_SSL; | ||
| 39 | char *gtls_buffer_hack; | ||
| 40 | int gtls_buffer_hack_len; | ||
| 41 | }; | ||
| 42 | |||
| 43 | iphone_lckd_client_t new_lockdownd_client(iphone_device_t phone); | ||
| 44 | iphone_error_t lockdownd_hello(iphone_lckd_client_t control); | ||
| 45 | iphone_error_t lockdownd_generic_get_value(iphone_lckd_client_t control, char *req_key, char *req_string, | ||
| 46 | gnutls_datum_t * value); | ||
| 47 | iphone_error_t lockdownd_get_device_uid(iphone_lckd_client_t control, char **uid); | ||
| 48 | iphone_error_t lockdownd_get_device_public_key(iphone_lckd_client_t control, gnutls_datum_t * public_key); | ||
| 49 | |||
| 50 | iphone_error_t lockdownd_gen_pair_cert(gnutls_datum_t public_key, gnutls_datum_t * device_cert, | ||
| 51 | gnutls_datum_t * host_cert, gnutls_datum_t * root_cert); | ||
| 52 | iphone_error_t lockdownd_pair_device(iphone_lckd_client_t control, char *uid, char *host_id); | ||
| 53 | void lockdownd_close(iphone_lckd_client_t control); | ||
| 54 | |||
| 55 | // SSL functions | ||
| 56 | |||
| 57 | iphone_error_t lockdownd_start_SSL_session(iphone_lckd_client_t control, const char *HostID); | ||
| 58 | ssize_t lockdownd_securead(gnutls_transport_ptr_t transport, char *buffer, size_t length); | ||
| 59 | ssize_t lockdownd_secuwrite(gnutls_transport_ptr_t transport, char *buffer, size_t length); | ||
| 60 | |||
| 61 | |||
| 62 | #endif | ||
diff --git a/src/usbmux.c b/src/usbmux.c deleted file mode 100644 index f0499fa..0000000 --- a/src/usbmux.c +++ /dev/null | |||
| @@ -1,381 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * usbmux.c | ||
| 3 | * Interprets the usb multiplexing protocol used by the iPhone. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <sys/types.h> | ||
| 23 | #include <arpa/inet.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <stdlib.h> | ||
| 26 | #include <string.h> | ||
| 27 | |||
| 28 | #include "usbmux.h" | ||
| 29 | |||
| 30 | static iphone_umux_client_t *connlist = NULL; | ||
| 31 | static int clients = 0; | ||
| 32 | |||
| 33 | /** Creates a USBMux packet for the given set of ports. | ||
| 34 | * | ||
| 35 | * @param s_port The source port for the connection. | ||
| 36 | * @param d_port The destination port for the connection. | ||
| 37 | * | ||
| 38 | * @return A USBMux packet | ||
| 39 | */ | ||
| 40 | usbmux_tcp_header *new_mux_packet(uint16_t s_port, uint16_t d_port) | ||
| 41 | { | ||
| 42 | usbmux_tcp_header *conn = (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header)); | ||
| 43 | conn->type = htonl(6); | ||
| 44 | conn->length = 28; | ||
| 45 | conn->sport = htons(s_port); | ||
| 46 | conn->dport = htons(d_port); | ||
| 47 | conn->scnt = 0; | ||
| 48 | conn->ocnt = 0; | ||
| 49 | conn->offset = 0x50; | ||
| 50 | conn->window = htons(0x0200); | ||
| 51 | conn->nullnull = 0x0000; | ||
| 52 | conn->length16 = 28; | ||
| 53 | return conn; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** Creates a USBMux header containing version information | ||
| 57 | * | ||
| 58 | * @return A USBMux header | ||
| 59 | */ | ||
| 60 | usbmux_version_header *version_header() | ||
| 61 | { | ||
| 62 | usbmux_version_header *version = (usbmux_version_header *) malloc(sizeof(usbmux_version_header)); | ||
| 63 | version->type = 0; | ||
| 64 | version->length = htonl(20); | ||
| 65 | version->major = htonl(1); | ||
| 66 | version->minor = 0; | ||
| 67 | version->allnull = 0; | ||
| 68 | return version; | ||
| 69 | } | ||
| 70 | |||
| 71 | |||
| 72 | // Maintenance functions. | ||
| 73 | |||
| 74 | /** Removes a connection from the list of connections made. | ||
| 75 | * The list of connections is necessary for buffering. | ||
| 76 | * | ||
| 77 | * @param connection The connection to delete from the tracking list. | ||
| 78 | */ | ||
| 79 | void delete_connection(iphone_umux_client_t connection) | ||
| 80 | { | ||
| 81 | iphone_umux_client_t *newlist = (iphone_umux_client_t *) malloc(sizeof(iphone_umux_client_t) * (clients - 1)); | ||
| 82 | int i = 0, j = 0; | ||
| 83 | for (i = 0; i < clients; i++) { | ||
| 84 | if (connlist[i] == connection) | ||
| 85 | continue; | ||
| 86 | else { | ||
| 87 | newlist[j] = connlist[i]; | ||
| 88 | j++; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | free(connlist); | ||
| 92 | connlist = newlist; | ||
| 93 | clients--; | ||
| 94 | if (connection->recv_buffer) | ||
| 95 | free(connection->recv_buffer); | ||
| 96 | if (connection->header) | ||
| 97 | free(connection->header); | ||
| 98 | connection->r_len = 0; | ||
| 99 | free(connection); | ||
| 100 | } | ||
| 101 | |||
| 102 | /** Adds a connection to the list of connections made. | ||
| 103 | * The connection list is necessary for buffering. | ||
| 104 | * | ||
| 105 | * @param connection The connection to add to the global list of connections. | ||
| 106 | */ | ||
| 107 | |||
| 108 | void add_connection(iphone_umux_client_t connection) | ||
| 109 | { | ||
| 110 | iphone_umux_client_t *newlist = | ||
| 111 | (iphone_umux_client_t *) realloc(connlist, sizeof(iphone_umux_client_t) * (clients + 1)); | ||
| 112 | newlist[clients] = connection; | ||
| 113 | connlist = newlist; | ||
| 114 | clients++; | ||
| 115 | } | ||
| 116 | |||
| 117 | /** Initializes a connection on phone, with source port s_port and destination port d_port | ||
| 118 | * | ||
| 119 | * @param device The iPhone to initialize a connection on. | ||
| 120 | * @param src_port The source port | ||
| 121 | * @param dst_port The destination port -- 0xf27e for lockdownd. | ||
| 122 | * @param client A mux TCP header for the connection which is used for tracking and data transfer. | ||
| 123 | * @return IPHONE_E_SUCCESS on success, an error code otherwise. | ||
| 124 | */ | ||
| 125 | iphone_error_t iphone_mux_new_client(iphone_device_t device, uint16_t src_port, uint16_t dst_port, | ||
| 126 | iphone_umux_client_t * client) | ||
| 127 | { | ||
| 128 | if (!device || !src_port || !dst_port) | ||
| 129 | return IPHONE_E_INVALID_ARG; | ||
| 130 | |||
| 131 | int bytes = 0; | ||
| 132 | // Initialize connection stuff | ||
| 133 | iphone_umux_client_t new_connection = (iphone_umux_client_t) malloc(sizeof(struct iphone_umux_client_int)); | ||
| 134 | new_connection->header = new_mux_packet(src_port, dst_port); | ||
| 135 | |||
| 136 | // blargg | ||
| 137 | if (new_connection && new_connection->header) { | ||
| 138 | new_connection->header->tcp_flags = 0x02; | ||
| 139 | new_connection->header->length = htonl(new_connection->header->length); | ||
| 140 | new_connection->header->length16 = htons(new_connection->header->length16); | ||
| 141 | |||
| 142 | if (send_to_phone(device, (char *) new_connection->header, sizeof(usbmux_tcp_header)) >= 0) { | ||
| 143 | usbmux_tcp_header *response; | ||
| 144 | response = (usbmux_tcp_header *) malloc(sizeof(usbmux_tcp_header)); | ||
| 145 | bytes = recv_from_phone(device, (char *) response, sizeof(*response)); | ||
| 146 | if (response->tcp_flags != 0x12) { | ||
| 147 | free(response); | ||
| 148 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 149 | } else { | ||
| 150 | free(response); | ||
| 151 | |||
| 152 | log_debug_msg("mux_connect: connection success\n"); | ||
| 153 | new_connection->header->tcp_flags = 0x10; | ||
| 154 | new_connection->header->scnt = 1; | ||
| 155 | new_connection->header->ocnt = 1; | ||
| 156 | new_connection->phone = device; | ||
| 157 | new_connection->recv_buffer = NULL; | ||
| 158 | new_connection->r_len = 0; | ||
| 159 | add_connection(new_connection); | ||
| 160 | *client = new_connection; | ||
| 161 | return IPHONE_E_SUCCESS; | ||
| 162 | } | ||
| 163 | } else { | ||
| 164 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | // if we get to this point it's probably bad | ||
| 168 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 169 | } | ||
| 170 | |||
| 171 | /** Cleans up the given USBMux connection. | ||
| 172 | * @note Once a connection is closed it may not be used again. | ||
| 173 | * | ||
| 174 | * @param connection The connection to close. | ||
| 175 | * | ||
| 176 | * @return IPHONE_E_SUCCESS on success. | ||
| 177 | */ | ||
| 178 | iphone_error_t iphone_mux_free_client(iphone_umux_client_t client) | ||
| 179 | { | ||
| 180 | if (!client || !client->phone) | ||
| 181 | return; | ||
| 182 | |||
| 183 | client->header->tcp_flags = 0x04; | ||
| 184 | client->header->scnt = htonl(client->header->scnt); | ||
| 185 | client->header->ocnt = htonl(client->header->ocnt); | ||
| 186 | int bytes = 0; | ||
| 187 | |||
| 188 | bytes = usb_bulk_write(client->phone->device, BULKOUT, (char *) client->header, sizeof(usbmux_tcp_header), 800); | ||
| 189 | if (bytes < 0) | ||
| 190 | log_debug_msg("iphone_muxèfree_client(): when writing, libusb gave me the error: %s\n", usb_strerror()); | ||
| 191 | |||
| 192 | bytes = usb_bulk_read(client->phone->device, BULKIN, (char *) client->header, sizeof(usbmux_tcp_header), 800); | ||
| 193 | if (bytes < 0) | ||
| 194 | log_debug_msg("get_iPhone(): when reading, libusb gave me the error: %s\n", usb_strerror()); | ||
| 195 | |||
| 196 | delete_connection(client); | ||
| 197 | |||
| 198 | return IPHONE_E_SUCCESS; | ||
| 199 | } | ||
| 200 | |||
| 201 | |||
| 202 | /** Sends the given data over the selected connection. | ||
| 203 | * | ||
| 204 | * @param phone The iPhone to send to. | ||
| 205 | * @param client The client we're sending data on. | ||
| 206 | * @param data A pointer to the data to send. | ||
| 207 | * @param datalen How much data we're sending. | ||
| 208 | * @param sent_bytes The number of bytes sent, minus the header (28) | ||
| 209 | * | ||
| 210 | * @return IPHONE_E_SUCCESS on success. | ||
| 211 | */ | ||
| 212 | |||
| 213 | iphone_error_t iphone_mux_send(iphone_umux_client_t client, const char *data, uint32_t datalen, uint32_t * sent_bytes) | ||
| 214 | { | ||
| 215 | if (!client->phone || !client || !data || datalen == 0 || !sent_bytes) | ||
| 216 | return IPHONE_E_INVALID_ARG; | ||
| 217 | // client->scnt and client->ocnt should already be in host notation... | ||
| 218 | // we don't need to change them juuuust yet. | ||
| 219 | *sent_bytes = 0; | ||
| 220 | log_debug_msg("mux_send(): client wants to send %i bytes\n", datalen); | ||
| 221 | char *buffer = (char *) malloc(sizeof(usbmux_tcp_header) + datalen + 2); // allow 2 bytes of safety padding | ||
| 222 | // Set the length and pre-emptively htonl/htons it | ||
| 223 | client->header->length = htonl(sizeof(usbmux_tcp_header) + datalen); | ||
| 224 | client->header->length16 = htons(sizeof(usbmux_tcp_header) + datalen); | ||
| 225 | |||
| 226 | // Put scnt and ocnt into big-endian notation | ||
| 227 | client->header->scnt = htonl(client->header->scnt); | ||
| 228 | client->header->ocnt = htonl(client->header->ocnt); | ||
| 229 | // Concatenation of stuff in the buffer. | ||
| 230 | memcpy(buffer, client->header, sizeof(usbmux_tcp_header)); | ||
| 231 | memcpy(buffer + sizeof(usbmux_tcp_header), data, datalen); | ||
| 232 | |||
| 233 | // We have a buffer full of data, we should now send it to the phone. | ||
| 234 | log_debug_msg("actually sending %zi bytes of data at %p\n", sizeof(usbmux_tcp_header) + datalen, buffer); | ||
| 235 | |||
| 236 | |||
| 237 | *sent_bytes = send_to_phone(client->phone, buffer, sizeof(usbmux_tcp_header) + datalen); | ||
| 238 | log_debug_msg("mux_send: sent %i bytes!\n", *sent_bytes); | ||
| 239 | // Now that we've sent it off, we can clean up after our sloppy selves. | ||
| 240 | dump_debug_buffer("packet", buffer, *sent_bytes); | ||
| 241 | if (buffer) | ||
| 242 | free(buffer); | ||
| 243 | // Re-calculate scnt and ocnt | ||
| 244 | client->header->scnt = ntohl(client->header->scnt) + datalen; | ||
| 245 | client->header->ocnt = ntohl(client->header->ocnt); | ||
| 246 | |||
| 247 | // Revert lengths | ||
| 248 | client->header->length = ntohl(client->header->length); | ||
| 249 | client->header->length16 = ntohs(client->header->length16); | ||
| 250 | |||
| 251 | // Now return the bytes. | ||
| 252 | if (*sent_bytes < sizeof(usbmux_tcp_header) + datalen) { | ||
| 253 | *sent_bytes = 0; | ||
| 254 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 255 | } else { | ||
| 256 | *sent_bytes = *sent_bytes - 28; // actual length sent. :/ | ||
| 257 | } | ||
| 258 | |||
| 259 | return IPHONE_E_SUCCESS; | ||
| 260 | } | ||
| 261 | |||
| 262 | /** This is a higher-level USBMuxTCP-like function | ||
| 263 | * | ||
| 264 | * @param connection The connection to receive data on. | ||
| 265 | * @param data Where to put the data we receive. | ||
| 266 | * @param datalen How much data to read. | ||
| 267 | * | ||
| 268 | * @return How many bytes were read, or -1 if something bad happens. | ||
| 269 | */ | ||
| 270 | iphone_error_t iphone_mux_recv(iphone_umux_client_t client, char *data, uint32_t datalen, uint32_t * recv_bytes) | ||
| 271 | { | ||
| 272 | |||
| 273 | if (!client || !data || datalen == 0 || !recv_bytes) | ||
| 274 | return IPHONE_E_INVALID_ARG; | ||
| 275 | /* | ||
| 276 | * Order of operation: | ||
| 277 | * 1.) Check if the client has a pre-received buffer. | ||
| 278 | * 2.) If so, fill data with the buffer, as much as needed. | ||
| 279 | * a.) Return quickly if the buffer has enough | ||
| 280 | * b.) If the buffer is only part of the datalen, get the rest of datalen (and if we can't, just return) | ||
| 281 | * 3.) If not, receive directly from the phone. | ||
| 282 | * a.) Check incoming packet's ports. If proper, follow proper buffering and receiving operation. | ||
| 283 | * b.) If not, find the client the ports belong to and fill that client's buffer, then return mux_recv with the same args to try again. | ||
| 284 | */ | ||
| 285 | log_debug_msg("mux_recv: datalen == %i\n", datalen); | ||
| 286 | int bytes = 0, i = 0, complex = 0, offset = 0; | ||
| 287 | *recv_bytes = 0; | ||
| 288 | char *buffer = NULL; | ||
| 289 | usbmux_tcp_header *header = NULL; | ||
| 290 | |||
| 291 | if (client->recv_buffer) { | ||
| 292 | if (client->r_len >= datalen) { | ||
| 293 | memcpy(data, client->recv_buffer, datalen); | ||
| 294 | if (client->r_len == datalen) { | ||
| 295 | // reset everything | ||
| 296 | free(client->recv_buffer); | ||
| 297 | client->r_len = 0; | ||
| 298 | client->recv_buffer = NULL; | ||
| 299 | } else { | ||
| 300 | buffer = (char *) malloc(sizeof(char) * (client->r_len - datalen)); | ||
| 301 | memcpy(buffer, client->recv_buffer + datalen, (client->r_len - datalen)); | ||
| 302 | client->r_len -= datalen; | ||
| 303 | free(client->recv_buffer); | ||
| 304 | client->recv_buffer = buffer; | ||
| 305 | } | ||
| 306 | |||
| 307 | // Since we were able to fill the data straight from our buffer, we can just return datalen. See 2a above. | ||
| 308 | return datalen; | ||
| 309 | } else { | ||
| 310 | memcpy(data, client->recv_buffer, client->r_len); | ||
| 311 | free(client->recv_buffer); // don't need to deal with anymore, but... | ||
| 312 | offset = client->r_len; // see #2b, above | ||
| 313 | client->r_len = 0; | ||
| 314 | } | ||
| 315 | } // End of what to do if we have a pre-buffer. See #1 and #2 above. | ||
| 316 | |||
| 317 | buffer = (char *) malloc(sizeof(char) * 131072); // make sure we get enough ;) | ||
| 318 | |||
| 319 | // See #3. | ||
| 320 | bytes = recv_from_phone(client->phone, buffer, 131072); | ||
| 321 | if (bytes < 28) { | ||
| 322 | free(buffer); | ||
| 323 | log_debug_msg("mux_recv: Did not even get the header.\n"); | ||
| 324 | return IPHONE_E_NOT_ENOUGH_DATA; | ||
| 325 | } | ||
| 326 | |||
| 327 | header = (usbmux_tcp_header *) buffer; | ||
| 328 | if (header->sport != client->header->dport || header->dport != client->header->sport) { | ||
| 329 | // Ooooops -- we got someone else's packet. | ||
| 330 | // We gotta stick it in their buffer. (Take that any old way you want ;) ) | ||
| 331 | for (i = 0; i < clients; i++) { | ||
| 332 | if (connlist[i]->header->sport == header->dport && connlist[i]->header->dport == header->sport) { | ||
| 333 | // we have a winner. | ||
| 334 | char *nfb = (char *) malloc(sizeof(char) * (connlist[i]->r_len + (bytes - 28))); | ||
| 335 | if (connlist[i]->recv_buffer && connlist[i]->r_len) { | ||
| 336 | memcpy(nfb, connlist[i]->recv_buffer, connlist[i]->r_len); | ||
| 337 | free(connlist[i]->recv_buffer); | ||
| 338 | } | ||
| 339 | connlist[i]->r_len += bytes - 28; | ||
| 340 | //connlist[i]->recv_buffer = (char*)realloc(connlist[i]->recv_buffer, sizeof(char) * client->r_len); // grow their buffer | ||
| 341 | connlist[i]->recv_buffer = nfb; | ||
| 342 | nfb = NULL; // A cookie for you if you can guess what "nfb" means. | ||
| 343 | complex = connlist[i]->r_len - (bytes - 28); | ||
| 344 | memcpy(connlist[i]->recv_buffer + complex, buffer + 28, bytes - 28); // paste into their buffer | ||
| 345 | connlist[i]->header->ocnt += bytes - 28; | ||
| 346 | } | ||
| 347 | } | ||
| 348 | // If it wasn't ours, it's been handled by this point... or forgotten. | ||
| 349 | // Free our buffer and continue. | ||
| 350 | free(buffer); | ||
| 351 | buffer = NULL; | ||
| 352 | return iphone_mux_recv(client, data, datalen, recv_bytes); // recurse back in to try again | ||
| 353 | } | ||
| 354 | // The packet was absolutely meant for us if it hits this point. | ||
| 355 | // The pre-buffer has been taken care of, so, again, if we're at this point we have to read from the phone. | ||
| 356 | |||
| 357 | if ((bytes - 28) > datalen) { | ||
| 358 | // Copy what we need into the data, buffer the rest because we can. | ||
| 359 | memcpy(data + offset, buffer + 28, datalen); // data+offset: see #2b, above | ||
| 360 | complex = client->r_len + (bytes - 28) - datalen; | ||
| 361 | client->recv_buffer = (char *) realloc(client->recv_buffer, (sizeof(char) * complex)); | ||
| 362 | client->r_len = complex; | ||
| 363 | complex = client->r_len - (bytes - 28) - datalen; | ||
| 364 | memcpy(client->recv_buffer + complex, buffer + 28 + datalen, (bytes - 28) - datalen); | ||
| 365 | free(buffer); | ||
| 366 | client->header->ocnt += bytes - 28; | ||
| 367 | *recv_bytes = datalen; | ||
| 368 | return IPHONE_E_SUCCESS; | ||
| 369 | } else { | ||
| 370 | // Fill the data with what we have, and just return. | ||
| 371 | memcpy(data + offset, buffer + 28, bytes - 28); // data+offset: see #2b, above | ||
| 372 | client->header->ocnt += bytes - 28; | ||
| 373 | free(buffer); | ||
| 374 | *recv_bytes = bytes - 28; | ||
| 375 | return IPHONE_E_SUCCESS; | ||
| 376 | } | ||
| 377 | |||
| 378 | // If we get to this point, 'tis probably bad. | ||
| 379 | log_debug_msg("mux_recv: Heisenbug: bytes and datalen not matching up\n"); | ||
| 380 | return IPHONE_E_UNKNOWN_ERROR; | ||
| 381 | } | ||
diff --git a/src/usbmux.h b/src/usbmux.h deleted file mode 100644 index 4b18e07..0000000 --- a/src/usbmux.h +++ /dev/null | |||
| @@ -1,58 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * usbmux.h | ||
| 3 | * Defines structures and variables pertaining to the usb multiplexing. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <sys/types.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #include <stdint.h> | ||
| 25 | #include "libiphone/libiphone.h" | ||
| 26 | |||
| 27 | #ifndef USBMUX_H | ||
| 28 | #define USBMUX_H | ||
| 29 | |||
| 30 | #ifndef IPHONE_H | ||
| 31 | #include "iphone.h" | ||
| 32 | #endif | ||
| 33 | |||
| 34 | typedef struct { | ||
| 35 | uint32_t type, length; | ||
| 36 | uint16_t sport, dport; | ||
| 37 | uint32_t scnt, ocnt; | ||
| 38 | uint8_t offset, tcp_flags; | ||
| 39 | uint16_t window, nullnull, length16; | ||
| 40 | } usbmux_tcp_header; | ||
| 41 | |||
| 42 | struct iphone_umux_client_int { | ||
| 43 | usbmux_tcp_header *header; | ||
| 44 | iphone_device_t phone; | ||
| 45 | char *recv_buffer; | ||
| 46 | int r_len; | ||
| 47 | }; | ||
| 48 | |||
| 49 | usbmux_tcp_header *new_mux_packet(uint16_t s_port, uint16_t d_port); | ||
| 50 | |||
| 51 | typedef struct { | ||
| 52 | uint32_t type, length, major, minor, allnull; | ||
| 53 | } usbmux_version_header; | ||
| 54 | |||
| 55 | usbmux_version_header *version_header(); | ||
| 56 | |||
| 57 | |||
| 58 | #endif | ||
diff --git a/src/userpref.c b/src/userpref.c deleted file mode 100644 index b707957..0000000 --- a/src/userpref.c +++ /dev/null | |||
| @@ -1,285 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * userpref.c | ||
| 3 | * contains methods to access user specific certificates IDs and more. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Jonathan Beck All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <glib.h> | ||
| 23 | #include <glib/gprintf.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <string.h> | ||
| 26 | #include "userpref.h" | ||
| 27 | #include "utils.h" | ||
| 28 | #include <string.h> | ||
| 29 | #include <stdlib.h> | ||
| 30 | |||
| 31 | #define LIBIPHONE_CONF_DIR "libiphone" | ||
| 32 | #define LIBIPHONE_CONF_FILE "libiphonerc" | ||
| 33 | |||
| 34 | #define LIBIPHONE_ROOT_PRIVKEY "RootPrivateKey.pem" | ||
| 35 | #define LIBIPHONE_HOST_PRIVKEY "HostPrivateKey.pem" | ||
| 36 | #define LIBIPHONE_ROOT_CERTIF "RootCertificate.pem" | ||
| 37 | #define LIBIPHONE_HOST_CERTIF "HostCertificate.pem" | ||
| 38 | |||
| 39 | |||
| 40 | /** Creates a freedesktop compatible configuration directory for libiphone. | ||
| 41 | */ | ||
| 42 | inline void create_config_dir() | ||
| 43 | { | ||
| 44 | gchar *config_dir = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, NULL); | ||
| 45 | |||
| 46 | if (!g_file_test(config_dir, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) | ||
| 47 | g_mkdir_with_parents(config_dir, 0755); | ||
| 48 | |||
| 49 | g_free(config_dir); | ||
| 50 | } | ||
| 51 | |||
| 52 | |||
| 53 | /** Reads the HostID from a previously generated configuration file. | ||
| 54 | * | ||
| 55 | * @note It is the responsibility of the calling function to free the returned host_id | ||
| 56 | * | ||
| 57 | * @return The string containing the HostID or NULL | ||
| 58 | */ | ||
| 59 | char *get_host_id() | ||
| 60 | { | ||
| 61 | char *host_id = NULL; | ||
| 62 | gchar *config_file; | ||
| 63 | GKeyFile *key_file; | ||
| 64 | gchar *loc_host_id; | ||
| 65 | |||
| 66 | config_file = | ||
| 67 | g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_CONF_FILE, NULL); | ||
| 68 | |||
| 69 | /* now parse file to get the HostID */ | ||
| 70 | key_file = g_key_file_new(); | ||
| 71 | if (g_key_file_load_from_file(key_file, config_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) { | ||
| 72 | loc_host_id = g_key_file_get_value(key_file, "Global", "HostID", NULL); | ||
| 73 | if (loc_host_id) | ||
| 74 | host_id = strdup((char *) loc_host_id); | ||
| 75 | g_free(loc_host_id); | ||
| 76 | } | ||
| 77 | g_key_file_free(key_file); | ||
| 78 | g_free(config_file); | ||
| 79 | |||
| 80 | log_debug_msg("get_host_id(): Using %s as HostID\n", host_id); | ||
| 81 | return host_id; | ||
| 82 | } | ||
| 83 | |||
| 84 | /** Determines whether this iPhone has been connected to this system before. | ||
| 85 | * | ||
| 86 | * @param uid The device uid as given by the iPhone. | ||
| 87 | * | ||
| 88 | * @return 1 if the iPhone has been connected previously to this configuration | ||
| 89 | * or 0 otherwise. | ||
| 90 | */ | ||
| 91 | int is_device_known(char *uid) | ||
| 92 | { | ||
| 93 | int ret = 0; | ||
| 94 | gchar *config_file; | ||
| 95 | GKeyFile *key_file; | ||
| 96 | gchar **devices_list, **pcur, *keyfilepath, *stored_key; | ||
| 97 | GIOChannel *keyfile; | ||
| 98 | |||
| 99 | /* first get config file */ | ||
| 100 | gchar *device_file = g_strconcat(uid, ".pem", NULL); | ||
| 101 | config_file = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, device_file, NULL); | ||
| 102 | if (g_file_test(config_file, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) | ||
| 103 | ret = 1; | ||
| 104 | g_free(config_file); | ||
| 105 | g_free(device_file); | ||
| 106 | return ret; | ||
| 107 | } | ||
| 108 | |||
| 109 | /** Mark the iPhone (as represented by the key) as having connected to this | ||
| 110 | * configuration. | ||
| 111 | * | ||
| 112 | * @param public_key The public key given by the iPhone | ||
| 113 | * | ||
| 114 | * @return 1 on success and 0 if no public key is given or if it has already | ||
| 115 | * been marked as connected previously. | ||
| 116 | */ | ||
| 117 | int store_device_public_key(char *uid, gnutls_datum_t public_key) | ||
| 118 | { | ||
| 119 | |||
| 120 | if (NULL == public_key.data || is_device_known(uid)) | ||
| 121 | return 0; | ||
| 122 | |||
| 123 | /* ensure config directory exists */ | ||
| 124 | create_config_dir(); | ||
| 125 | |||
| 126 | /* build file path */ | ||
| 127 | gchar *device_file = g_strconcat(uid, ".pem", NULL); | ||
| 128 | gchar *pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, device_file, NULL); | ||
| 129 | |||
| 130 | /* store file */ | ||
| 131 | FILE *pFile = fopen(pem, "wb"); | ||
| 132 | fwrite(public_key.data, 1, public_key.size, pFile); | ||
| 133 | fclose(pFile); | ||
| 134 | g_free(pem); | ||
| 135 | g_free(device_file); | ||
| 136 | return 1; | ||
| 137 | } | ||
| 138 | |||
| 139 | /** Private function which reads the given file into a gnutls structure. | ||
| 140 | * | ||
| 141 | * @param file The filename of the file to read | ||
| 142 | * @param data The pointer at which to store the data. | ||
| 143 | * | ||
| 144 | * @return 1 if the file contents where read successfully and 0 otherwise. | ||
| 145 | */ | ||
| 146 | int read_file_in_confdir(char *file, gnutls_datum_t * data) | ||
| 147 | { | ||
| 148 | gboolean success; | ||
| 149 | gsize size; | ||
| 150 | char *content; | ||
| 151 | gchar *filepath; | ||
| 152 | |||
| 153 | if (NULL == file || NULL == data) | ||
| 154 | return 0; | ||
| 155 | |||
| 156 | /* Read file */ | ||
| 157 | filepath = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, file, NULL); | ||
| 158 | success = g_file_get_contents(filepath, &content, &size, NULL); | ||
| 159 | g_free(filepath); | ||
| 160 | |||
| 161 | /* Add it to the gnutls_datnum_t structure */ | ||
| 162 | data->data = content; | ||
| 163 | data->size = size; | ||
| 164 | |||
| 165 | return success; | ||
| 166 | } | ||
| 167 | |||
| 168 | /** Read the root private key | ||
| 169 | * | ||
| 170 | * @param root_privkey A pointer to the appropriate gnutls structure | ||
| 171 | * | ||
| 172 | * @return 1 if the file was successfully read and 0 otherwise. | ||
| 173 | */ | ||
| 174 | int get_root_private_key(gnutls_datum_t * root_privkey) | ||
| 175 | { | ||
| 176 | return read_file_in_confdir(LIBIPHONE_ROOT_PRIVKEY, root_privkey); | ||
| 177 | } | ||
| 178 | |||
| 179 | /** Read the host private key | ||
| 180 | * | ||
| 181 | * @param host_privkey A pointer to the appropriate gnutls structure | ||
| 182 | * | ||
| 183 | * @return 1 if the file was successfully read and 0 otherwise. | ||
| 184 | */ | ||
| 185 | int get_host_private_key(gnutls_datum_t * host_privkey) | ||
| 186 | { | ||
| 187 | return read_file_in_confdir(LIBIPHONE_HOST_PRIVKEY, host_privkey); | ||
| 188 | } | ||
| 189 | |||
| 190 | /** Read the root certificate | ||
| 191 | * | ||
| 192 | * @param root_privkey A pointer to the appropriate gnutls structure | ||
| 193 | * | ||
| 194 | * @return 1 if the file was successfully read and 0 otherwise. | ||
| 195 | */ | ||
| 196 | int get_root_certificate(gnutls_datum_t * root_cert) | ||
| 197 | { | ||
| 198 | return read_file_in_confdir(LIBIPHONE_ROOT_CERTIF, root_cert); | ||
| 199 | } | ||
| 200 | |||
| 201 | /** Read the host certificate | ||
| 202 | * | ||
| 203 | * @param root_privkey A pointer to the appropriate gnutls structure | ||
| 204 | * | ||
| 205 | * @return 1 if the file was successfully read and 0 otherwise. | ||
| 206 | */ | ||
| 207 | int get_host_certificate(gnutls_datum_t * host_cert) | ||
| 208 | { | ||
| 209 | return read_file_in_confdir(LIBIPHONE_HOST_CERTIF, host_cert); | ||
| 210 | } | ||
| 211 | |||
| 212 | /** Create and save a configuration file containing the given data. | ||
| 213 | * | ||
| 214 | * @note: All fields must specified and be non-null | ||
| 215 | * | ||
| 216 | * @param host_id The UUID of the host | ||
| 217 | * @param root_key The root key | ||
| 218 | * @param host_key The host key | ||
| 219 | * @param root_cert The root certificate | ||
| 220 | * @param host_cert The host certificate | ||
| 221 | * | ||
| 222 | * @return 1 on success and 0 otherwise. | ||
| 223 | */ | ||
| 224 | int init_config_file(char *host_id, gnutls_datum_t * root_key, gnutls_datum_t * host_key, gnutls_datum_t * root_cert, | ||
| 225 | gnutls_datum_t * host_cert) | ||
| 226 | { | ||
| 227 | FILE *pFile; | ||
| 228 | gchar *pem; | ||
| 229 | GKeyFile *key_file; | ||
| 230 | gsize length; | ||
| 231 | gchar *buf, *config_file; | ||
| 232 | GIOChannel *file; | ||
| 233 | |||
| 234 | if (!host_id || !root_key || !host_key || !root_cert || !host_cert) | ||
| 235 | return 0; | ||
| 236 | |||
| 237 | /* Make sure config directory exists */ | ||
| 238 | create_config_dir(); | ||
| 239 | |||
| 240 | /* Now parse file to get the HostID */ | ||
| 241 | key_file = g_key_file_new(); | ||
| 242 | |||
| 243 | /* Store in config file */ | ||
| 244 | log_debug_msg("init_config_file(): setting hostID to %s\n", host_id); | ||
| 245 | g_key_file_set_value(key_file, "Global", "HostID", host_id); | ||
| 246 | |||
| 247 | /* Write config file on disk */ | ||
| 248 | buf = g_key_file_to_data(key_file, &length, NULL); | ||
| 249 | config_file = | ||
| 250 | g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_CONF_FILE, NULL); | ||
| 251 | file = g_io_channel_new_file(config_file, "w", NULL); | ||
| 252 | g_free(config_file); | ||
| 253 | g_io_channel_write_chars(file, buf, length, NULL, NULL); | ||
| 254 | g_io_channel_shutdown(file, TRUE, NULL); | ||
| 255 | g_io_channel_unref(file); | ||
| 256 | |||
| 257 | g_key_file_free(key_file); | ||
| 258 | |||
| 259 | /* Now write keys and certificates to disk */ | ||
| 260 | pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_ROOT_PRIVKEY, NULL); | ||
| 261 | pFile = fopen(pem, "wb"); | ||
| 262 | fwrite(root_key->data, 1, root_key->size, pFile); | ||
| 263 | fclose(pFile); | ||
| 264 | g_free(pem); | ||
| 265 | |||
| 266 | pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_HOST_PRIVKEY, NULL); | ||
| 267 | pFile = fopen(pem, "wb"); | ||
| 268 | fwrite(host_key->data, 1, host_key->size, pFile); | ||
| 269 | fclose(pFile); | ||
| 270 | g_free(pem); | ||
| 271 | |||
| 272 | pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_ROOT_CERTIF, NULL); | ||
| 273 | pFile = fopen(pem, "wb"); | ||
| 274 | fwrite(root_cert->data, 1, root_cert->size, pFile); | ||
| 275 | fclose(pFile); | ||
| 276 | g_free(pem); | ||
| 277 | |||
| 278 | pem = g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), LIBIPHONE_CONF_DIR, LIBIPHONE_HOST_CERTIF, NULL); | ||
| 279 | pFile = fopen(pem, "wb"); | ||
| 280 | fwrite(host_cert->data, 1, host_cert->size, pFile); | ||
| 281 | fclose(pFile); | ||
| 282 | g_free(pem); | ||
| 283 | |||
| 284 | return 1; | ||
| 285 | } | ||
diff --git a/src/userpref.h b/src/userpref.h deleted file mode 100644 index 450549f..0000000 --- a/src/userpref.h +++ /dev/null | |||
| @@ -1,71 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * userpref.h | ||
| 3 | * contains methods to access user specific certificates IDs and more. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2008 Jonathan Beck All Rights Reserved. | ||
| 6 | * | ||
| 7 | * This library is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU Lesser General Public | ||
| 9 | * License as published by the Free Software Foundation; either | ||
| 10 | * version 2.1 of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This library is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * Lesser General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU Lesser General Public | ||
| 18 | * License along with this library; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 20 | */ | ||
| 21 | |||
| 22 | #ifndef USERPREF_H | ||
| 23 | #define USERPREF_H | ||
| 24 | |||
| 25 | #include <gnutls/gnutls.h> | ||
| 26 | /** | ||
| 27 | * Method to get user's HostID. Caller must free returned buffer. | ||
| 28 | * | ||
| 29 | * @return the HostID if exist in config file. Returns NULL otherwise. | ||
| 30 | */ | ||
| 31 | char *get_host_id(); | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Determine if we already paired this device. | ||
| 35 | * | ||
| 36 | * @return 1 if device is already paired. Returns 0 otherwise. | ||
| 37 | */ | ||
| 38 | int is_device_known(char *uid); | ||
| 39 | |||
| 40 | /** | ||
| 41 | * @return 1 if everything went well. Returns 0 otherwise. | ||
| 42 | */ | ||
| 43 | int store_device_public_key(char *uid, gnutls_datum_t public_key); | ||
| 44 | |||
| 45 | /** | ||
| 46 | * @return 1 if everything went well. Returns 0 otherwise. | ||
| 47 | */ | ||
| 48 | int get_root_private_key(gnutls_datum_t * root_privkey); | ||
| 49 | |||
| 50 | /** | ||
| 51 | * @return 1 if everything went well. Returns 0 otherwise. | ||
| 52 | */ | ||
| 53 | int get_host_private_key(gnutls_datum_t * host_privkey); | ||
| 54 | |||
| 55 | /** | ||
| 56 | * @return 1 if everything went well. Returns 0 otherwise. | ||
| 57 | */ | ||
| 58 | int get_root_certificate(gnutls_datum_t * root_cert); | ||
| 59 | |||
| 60 | /** | ||
| 61 | * @return 1 if everything went well. Returns 0 otherwise. | ||
| 62 | */ | ||
| 63 | int get_host_certificate(gnutls_datum_t * host_cert); | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Setup a brand new config file. | ||
| 67 | * @return 1 if everything went well. Returns 0 otherwise. | ||
| 68 | */ | ||
| 69 | int init_config_file(char *host_id, gnutls_datum_t * root_key, gnutls_datum_t * host_key, gnutls_datum_t * root_cert, | ||
| 70 | gnutls_datum_t * host_cert); | ||
| 71 | #endif | ||
