diff options
Diffstat (limited to 'src/idevice.c')
| -rw-r--r-- | src/idevice.c | 1470 |
1 files changed, 1189 insertions, 281 deletions
diff --git a/src/idevice.c b/src/idevice.c index 5a9d49b..0af27fd 100644 --- a/src/idevice.c +++ b/src/idevice.c | |||
| @@ -1,98 +1,377 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * idevice.c | 2 | * idevice.c |
| 3 | * Device discovery and communication interface. | 3 | * Device discovery and communication interface. |
| 4 | * | 4 | * |
| 5 | * Copyright (c) 2009-2021 Nikias Bassen. All Rights Reserved. | ||
| 6 | * Copyright (c) 2014 Martin Szulecki All Rights Reserved. | ||
| 5 | * Copyright (c) 2008 Zach C. All Rights Reserved. | 7 | * Copyright (c) 2008 Zach C. All Rights Reserved. |
| 6 | * Copyright (c) 2009 Nikias Bassen. All Rights Reserved. | ||
| 7 | * | 8 | * |
| 8 | * This library is free software; you can redistribute it and/or | 9 | * This library is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU Lesser General Public | 10 | * modify it under the terms of the GNU Lesser General Public |
| 10 | * License as published by the Free Software Foundation; either | 11 | * License as published by the Free Software Foundation; either |
| 11 | * version 2.1 of the License, or (at your option) any later version. | 12 | * version 2.1 of the License, or (at your option) any later version. |
| 12 | * | 13 | * |
| 13 | * This library is distributed in the hope that it will be useful, | 14 | * This library is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | * Lesser General Public License for more details. | 17 | * Lesser General Public License for more details. |
| 17 | * | 18 | * |
| 18 | * You should have received a copy of the GNU Lesser General Public | 19 | * You should have received a copy of the GNU Lesser General Public |
| 19 | * License along with this library; if not, write to the Free Software | 20 | * License along with this library; if not, write to the Free Software |
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 21 | */ | 22 | */ |
| 22 | 23 | ||
| 24 | #ifdef HAVE_CONFIG_H | ||
| 25 | #include <config.h> | ||
| 26 | #endif | ||
| 27 | |||
| 23 | #include <stdlib.h> | 28 | #include <stdlib.h> |
| 24 | #include <string.h> | 29 | #include <string.h> |
| 25 | #include <errno.h> | 30 | #include <errno.h> |
| 31 | #include <time.h> | ||
| 32 | |||
| 33 | #ifdef _WIN32 | ||
| 34 | #include <winsock2.h> | ||
| 35 | #include <ws2tcpip.h> | ||
| 36 | #include <windows.h> | ||
| 37 | #else | ||
| 38 | #include <sys/socket.h> | ||
| 39 | #include <netinet/in.h> | ||
| 40 | #endif | ||
| 26 | 41 | ||
| 27 | #include <usbmuxd.h> | 42 | #include <usbmuxd.h> |
| 43 | |||
| 44 | #if defined(HAVE_OPENSSL) | ||
| 45 | #include <openssl/err.h> | ||
| 46 | #include <openssl/rsa.h> | ||
| 47 | #include <openssl/ssl.h> | ||
| 48 | #elif defined(HAVE_GNUTLS) | ||
| 28 | #include <gnutls/gnutls.h> | 49 | #include <gnutls/gnutls.h> |
| 50 | #elif defined(HAVE_MBEDTLS) | ||
| 51 | #include <mbedtls/rsa.h> | ||
| 52 | #include <mbedtls/ssl.h> | ||
| 53 | #include <mbedtls/entropy.h> | ||
| 54 | #include <mbedtls/ctr_drbg.h> | ||
| 55 | #include <mbedtls/debug.h> | ||
| 56 | #else | ||
| 57 | #error No supported TLS/SSL library enabled | ||
| 58 | #endif | ||
| 59 | |||
| 60 | #include <libimobiledevice-glue/socket.h> | ||
| 61 | #include <libimobiledevice-glue/thread.h> | ||
| 62 | |||
| 29 | #include "idevice.h" | 63 | #include "idevice.h" |
| 30 | #include "userpref.h" | 64 | #include "lockdown.h" |
| 31 | #include "debug.h" | 65 | #include "common/userpref.h" |
| 66 | #include "common/debug.h" | ||
| 67 | |||
| 68 | #ifndef ECONNREFUSED | ||
| 69 | #define ECONNREFUSED 107 | ||
| 70 | #endif | ||
| 71 | #ifndef ETIMEDOUT | ||
| 72 | #define ETIMEDOUT 138 | ||
| 73 | #endif | ||
| 74 | |||
| 75 | |||
| 76 | #ifdef HAVE_OPENSSL | ||
| 77 | |||
| 78 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || \ | ||
| 79 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20020000L)) | ||
| 80 | #define TLS_method TLSv1_method | ||
| 81 | #endif | ||
| 82 | |||
| 83 | #if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) | ||
| 84 | static void SSL_COMP_free_compression_methods(void) | ||
| 85 | { | ||
| 86 | sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); | ||
| 87 | } | ||
| 88 | #endif | ||
| 89 | |||
| 90 | static void openssl_remove_thread_state(void) | ||
| 91 | { | ||
| 92 | /* ERR_remove_thread_state() is available since OpenSSL 1.0.0-beta1, but | ||
| 93 | * deprecated in OpenSSL 1.1.0 */ | ||
| 94 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
| 95 | #if OPENSSL_VERSION_NUMBER >= 0x10000001L | ||
| 96 | ERR_remove_thread_state(NULL); | ||
| 97 | #else | ||
| 98 | ERR_remove_state(0); | ||
| 99 | #endif | ||
| 100 | #endif | ||
| 101 | } | ||
| 102 | |||
| 103 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
| 104 | static mutex_t *mutex_buf = NULL; | ||
| 105 | static void locking_function(int mode, int n, const char* file, int line) | ||
| 106 | { | ||
| 107 | if (mode & CRYPTO_LOCK) | ||
| 108 | mutex_lock(&mutex_buf[n]); | ||
| 109 | else | ||
| 110 | mutex_unlock(&mutex_buf[n]); | ||
| 111 | } | ||
| 112 | |||
| 113 | #if OPENSSL_VERSION_NUMBER < 0x10000000L | ||
| 114 | static unsigned long id_function(void) | ||
| 115 | { | ||
| 116 | return ((unsigned long)THREAD_ID); | ||
| 117 | } | ||
| 118 | #else | ||
| 119 | static void id_function(CRYPTO_THREADID *thread) | ||
| 120 | { | ||
| 121 | CRYPTO_THREADID_set_numeric(thread, (unsigned long)THREAD_ID); | ||
| 122 | } | ||
| 123 | #endif | ||
| 124 | #endif | ||
| 125 | #endif /* HAVE_OPENSSL */ | ||
| 126 | |||
| 127 | // Reference: https://stackoverflow.com/a/2390626/1806760 | ||
| 128 | // Initializer/finalizer sample for MSVC and GCC/Clang. | ||
| 129 | // 2010-2016 Joe Lowe. Released into the public domain. | ||
| 130 | |||
| 131 | #ifdef __cplusplus | ||
| 132 | #define INITIALIZER(f) \ | ||
| 133 | static void f(void); \ | ||
| 134 | struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ | ||
| 135 | static void f(void) | ||
| 136 | #elif defined(_MSC_VER) | ||
| 137 | #pragma section(".CRT$XCU",read) | ||
| 138 | #define INITIALIZER2_(f,p) \ | ||
| 139 | static void f(void); \ | ||
| 140 | __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ | ||
| 141 | __pragma(comment(linker,"/include:" p #f "_")) \ | ||
| 142 | static void f(void) | ||
| 143 | #ifdef _WIN64 | ||
| 144 | #define INITIALIZER(f) INITIALIZER2_(f,"") | ||
| 145 | #else | ||
| 146 | #define INITIALIZER(f) INITIALIZER2_(f,"_") | ||
| 147 | #endif | ||
| 148 | #else | ||
| 149 | #define INITIALIZER(f) \ | ||
| 150 | static void f(void) __attribute__((__constructor__)); \ | ||
| 151 | static void f(void) | ||
| 152 | #endif | ||
| 153 | |||
| 154 | static void internal_idevice_deinit(void) | ||
| 155 | { | ||
| 156 | #if defined(HAVE_OPENSSL) | ||
| 157 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
| 158 | int i; | ||
| 159 | if (mutex_buf) { | ||
| 160 | #if OPENSSL_VERSION_NUMBER < 0x10000000L | ||
| 161 | CRYPTO_set_id_callback(NULL); | ||
| 162 | #else | ||
| 163 | CRYPTO_THREADID_set_callback(NULL); | ||
| 164 | #endif | ||
| 165 | CRYPTO_set_locking_callback(NULL); | ||
| 166 | for (i = 0; i < CRYPTO_num_locks(); i++) | ||
| 167 | mutex_destroy(&mutex_buf[i]); | ||
| 168 | free(mutex_buf); | ||
| 169 | mutex_buf = NULL; | ||
| 170 | } | ||
| 171 | |||
| 172 | EVP_cleanup(); | ||
| 173 | CRYPTO_cleanup_all_ex_data(); | ||
| 174 | SSL_COMP_free_compression_methods(); | ||
| 175 | openssl_remove_thread_state(); | ||
| 176 | #endif | ||
| 177 | #elif defined(HAVE_GNUTLS) | ||
| 178 | gnutls_global_deinit(); | ||
| 179 | #elif defined(HAVE_MBEDTLS) | ||
| 180 | // NO-OP | ||
| 181 | #endif | ||
| 182 | } | ||
| 183 | |||
| 184 | INITIALIZER(internal_idevice_init) | ||
| 185 | { | ||
| 186 | #if defined(HAVE_OPENSSL) | ||
| 187 | #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) | ||
| 188 | int i; | ||
| 189 | SSL_library_init(); | ||
| 190 | |||
| 191 | mutex_buf = malloc(CRYPTO_num_locks() * sizeof(mutex_t)); | ||
| 192 | if (!mutex_buf) | ||
| 193 | return; | ||
| 194 | for (i = 0; i < CRYPTO_num_locks(); i++) | ||
| 195 | mutex_init(&mutex_buf[i]); | ||
| 196 | |||
| 197 | #if OPENSSL_VERSION_NUMBER < 0x10000000L | ||
| 198 | CRYPTO_set_id_callback(id_function); | ||
| 199 | #else | ||
| 200 | CRYPTO_THREADID_set_callback(id_function); | ||
| 201 | #endif | ||
| 202 | CRYPTO_set_locking_callback(locking_function); | ||
| 203 | #endif | ||
| 204 | #elif defined(HAVE_GNUTLS) | ||
| 205 | gnutls_global_init(); | ||
| 206 | #elif defined(HAVE_MBEDTLS) | ||
| 207 | // NO-OP | ||
| 208 | #endif | ||
| 209 | atexit(internal_idevice_deinit); | ||
| 210 | } | ||
| 211 | |||
| 212 | const char* libimobiledevice_version() | ||
| 213 | { | ||
| 214 | #ifndef PACKAGE_VERSION | ||
| 215 | #error PACKAGE_VERSION is not defined! | ||
| 216 | #endif | ||
| 217 | return PACKAGE_VERSION; | ||
| 218 | } | ||
| 219 | |||
| 220 | struct idevice_subscription_context { | ||
| 221 | idevice_event_cb_t callback; | ||
| 222 | void *user_data; | ||
| 223 | usbmuxd_subscription_context_t ctx; | ||
| 224 | }; | ||
| 32 | 225 | ||
| 33 | static idevice_event_cb_t event_cb = NULL; | 226 | static idevice_subscription_context_t event_ctx = NULL; |
| 34 | 227 | ||
| 35 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) | 228 | static void usbmux_event_cb(const usbmuxd_event_t *event, void *user_data) |
| 36 | { | 229 | { |
| 230 | idevice_subscription_context_t context = (idevice_subscription_context_t)user_data; | ||
| 37 | idevice_event_t ev; | 231 | idevice_event_t ev; |
| 38 | 232 | ||
| 39 | ev.event = event->event; | 233 | ev.event = event->event; |
| 40 | ev.uuid = event->device.uuid; | 234 | ev.udid = event->device.udid; |
| 41 | ev.conn_type = CONNECTION_USBMUXD; | 235 | ev.conn_type = 0; |
| 236 | if (event->device.conn_type == CONNECTION_TYPE_USB) { | ||
| 237 | ev.conn_type = CONNECTION_USBMUXD; | ||
| 238 | } else if (event->device.conn_type == CONNECTION_TYPE_NETWORK) { | ||
| 239 | ev.conn_type = CONNECTION_NETWORK; | ||
| 240 | } else { | ||
| 241 | debug_info("Unknown connection type %d", event->device.conn_type); | ||
| 242 | } | ||
| 42 | 243 | ||
| 43 | if (event_cb) { | 244 | if (context->callback) { |
| 44 | event_cb(&ev, user_data); | 245 | context->callback(&ev, context->user_data); |
| 45 | } | 246 | } |
| 46 | } | 247 | } |
| 47 | 248 | ||
| 48 | /** | 249 | idevice_error_t idevice_events_subscribe(idevice_subscription_context_t *context, idevice_event_cb_t callback, void *user_data) |
| 49 | * Register a callback function that will be called when device add/remove | ||
| 50 | * events occur. | ||
| 51 | * | ||
| 52 | * @param callback Callback function to call. | ||
| 53 | * @param user_data Application-specific data passed as parameter | ||
| 54 | * to the registered callback function. | ||
| 55 | * | ||
| 56 | * @return IDEVICE_E_SUCCESS on success or an error value when an error occured. | ||
| 57 | */ | ||
| 58 | idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data) | ||
| 59 | { | 250 | { |
| 60 | event_cb = callback; | 251 | if (!context || !callback) { |
| 61 | int res = usbmuxd_subscribe(usbmux_event_cb, user_data); | 252 | return IDEVICE_E_INVALID_ARG; |
| 62 | if (res != 0) { | 253 | } |
| 63 | event_cb = NULL; | 254 | *context = malloc(sizeof(struct idevice_subscription_context)); |
| 64 | debug_info("Error %d when subscribing usbmux event callback!", res); | 255 | if (!*context) { |
| 256 | debug_info("ERROR: %s: Failed to allocate subscription context\n", __func__); | ||
| 257 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 258 | } | ||
| 259 | (*context)->callback = callback; | ||
| 260 | (*context)->user_data = user_data; | ||
| 261 | int res = usbmuxd_events_subscribe(&(*context)->ctx, usbmux_event_cb, *context); | ||
| 262 | if (res != 0) { | ||
| 263 | free(*context); | ||
| 264 | *context = NULL; | ||
| 265 | debug_info("ERROR: usbmuxd_subscribe() returned %d!", res); | ||
| 65 | return IDEVICE_E_UNKNOWN_ERROR; | 266 | return IDEVICE_E_UNKNOWN_ERROR; |
| 66 | } | 267 | } |
| 67 | return IDEVICE_E_SUCCESS; | 268 | return IDEVICE_E_SUCCESS; |
| 68 | } | 269 | } |
| 69 | 270 | ||
| 70 | /** | 271 | idevice_error_t idevice_events_unsubscribe(idevice_subscription_context_t context) |
| 71 | * Release the event callback function that has been registered with | ||
| 72 | * idevice_event_subscribe(). | ||
| 73 | * | ||
| 74 | * @return IDEVICE_E_SUCCESS on success or an error value when an error occured. | ||
| 75 | */ | ||
| 76 | idevice_error_t idevice_event_unsubscribe() | ||
| 77 | { | 272 | { |
| 78 | event_cb = NULL; | 273 | if (!context) { |
| 79 | int res = usbmuxd_unsubscribe(); | 274 | return IDEVICE_E_INVALID_ARG; |
| 275 | } | ||
| 276 | int res = usbmuxd_events_unsubscribe(context->ctx); | ||
| 80 | if (res != 0) { | 277 | if (res != 0) { |
| 81 | debug_info("Error %d when unsubscribing usbmux event callback!", res); | 278 | debug_info("ERROR: usbmuxd_unsubscribe() returned %d!", res); |
| 82 | return IDEVICE_E_UNKNOWN_ERROR; | 279 | return IDEVICE_E_UNKNOWN_ERROR; |
| 83 | } | 280 | } |
| 281 | if (context == event_ctx) { | ||
| 282 | event_ctx = NULL; | ||
| 283 | } | ||
| 284 | free(context); | ||
| 285 | return IDEVICE_E_SUCCESS; | ||
| 286 | } | ||
| 287 | |||
| 288 | idevice_error_t idevice_event_subscribe(idevice_event_cb_t callback, void *user_data) | ||
| 289 | { | ||
| 290 | if (event_ctx) { | ||
| 291 | idevice_events_unsubscribe(event_ctx); | ||
| 292 | } | ||
| 293 | return idevice_events_subscribe(&event_ctx, callback, user_data); | ||
| 294 | } | ||
| 295 | |||
| 296 | idevice_error_t idevice_event_unsubscribe(void) | ||
| 297 | { | ||
| 298 | if (!event_ctx) { | ||
| 299 | return IDEVICE_E_SUCCESS; | ||
| 300 | } | ||
| 301 | event_ctx->callback = NULL; | ||
| 302 | return idevice_events_unsubscribe(event_ctx); | ||
| 303 | } | ||
| 304 | |||
| 305 | idevice_error_t idevice_get_device_list_extended(idevice_info_t **devices, int *count) | ||
| 306 | { | ||
| 307 | usbmuxd_device_info_t *dev_list; | ||
| 308 | |||
| 309 | *devices = NULL; | ||
| 310 | *count = 0; | ||
| 311 | |||
| 312 | if (usbmuxd_get_device_list(&dev_list) < 0) { | ||
| 313 | debug_info("ERROR: usbmuxd is not running!", __func__); | ||
| 314 | return IDEVICE_E_NO_DEVICE; | ||
| 315 | } | ||
| 316 | |||
| 317 | idevice_info_t *newlist = NULL; | ||
| 318 | int i, newcount = 0; | ||
| 319 | |||
| 320 | for (i = 0; dev_list[i].handle > 0; i++) { | ||
| 321 | newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); | ||
| 322 | newlist[newcount] = malloc(sizeof(struct idevice_info)); | ||
| 323 | newlist[newcount]->udid = strdup(dev_list[i].udid); | ||
| 324 | if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { | ||
| 325 | newlist[newcount]->conn_type = CONNECTION_USBMUXD; | ||
| 326 | newlist[newcount]->conn_data = NULL; | ||
| 327 | } else if (dev_list[i].conn_type == CONNECTION_TYPE_NETWORK) { | ||
| 328 | newlist[newcount]->conn_type = CONNECTION_NETWORK; | ||
| 329 | struct sockaddr* saddr = (struct sockaddr*)(dev_list[i].conn_data); | ||
| 330 | size_t addrlen = 0; | ||
| 331 | switch (saddr->sa_family) { | ||
| 332 | case AF_INET: | ||
| 333 | addrlen = sizeof(struct sockaddr_in); | ||
| 334 | break; | ||
| 335 | #ifdef AF_INET6 | ||
| 336 | case AF_INET6: | ||
| 337 | addrlen = sizeof(struct sockaddr_in6); | ||
| 338 | break; | ||
| 339 | #endif | ||
| 340 | default: | ||
| 341 | debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); | ||
| 342 | continue; | ||
| 343 | } | ||
| 344 | newlist[newcount]->conn_data = malloc(addrlen); | ||
| 345 | memcpy(newlist[newcount]->conn_data, dev_list[i].conn_data, addrlen); | ||
| 346 | } | ||
| 347 | newcount++; | ||
| 348 | *devices = newlist; | ||
| 349 | } | ||
| 350 | usbmuxd_device_list_free(&dev_list); | ||
| 351 | |||
| 352 | *count = newcount; | ||
| 353 | newlist = realloc(*devices, sizeof(idevice_info_t) * (newcount+1)); | ||
| 354 | newlist[newcount] = NULL; | ||
| 355 | *devices = newlist; | ||
| 356 | |||
| 357 | return IDEVICE_E_SUCCESS; | ||
| 358 | } | ||
| 359 | |||
| 360 | idevice_error_t idevice_device_list_extended_free(idevice_info_t *devices) | ||
| 361 | { | ||
| 362 | if (devices) { | ||
| 363 | int i = 0; | ||
| 364 | while (devices[i]) { | ||
| 365 | free(devices[i]->udid); | ||
| 366 | free(devices[i]->conn_data); | ||
| 367 | free(devices[i]); | ||
| 368 | i++; | ||
| 369 | } | ||
| 370 | free(devices); | ||
| 371 | } | ||
| 84 | return IDEVICE_E_SUCCESS; | 372 | return IDEVICE_E_SUCCESS; |
| 85 | } | 373 | } |
| 86 | 374 | ||
| 87 | /** | ||
| 88 | * Get a list of currently available devices. | ||
| 89 | * | ||
| 90 | * @param devices List of uuids of devices that are currently available. | ||
| 91 | * This list is terminated by a NULL pointer. | ||
| 92 | * @param count Number of devices found. | ||
| 93 | * | ||
| 94 | * @return IDEVICE_E_SUCCESS on success or an error value when an error occured. | ||
| 95 | */ | ||
| 96 | idevice_error_t idevice_get_device_list(char ***devices, int *count) | 375 | idevice_error_t idevice_get_device_list(char ***devices, int *count) |
| 97 | { | 376 | { |
| 98 | usbmuxd_device_info_t *dev_list; | 377 | usbmuxd_device_info_t *dev_list; |
| @@ -101,7 +380,7 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
| 101 | *count = 0; | 380 | *count = 0; |
| 102 | 381 | ||
| 103 | if (usbmuxd_get_device_list(&dev_list) < 0) { | 382 | if (usbmuxd_get_device_list(&dev_list) < 0) { |
| 104 | debug_info("ERROR: usbmuxd is not running!\n", __func__); | 383 | debug_info("ERROR: usbmuxd is not running!", __func__); |
| 105 | return IDEVICE_E_NO_DEVICE; | 384 | return IDEVICE_E_NO_DEVICE; |
| 106 | } | 385 | } |
| 107 | 386 | ||
| @@ -109,9 +388,11 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
| 109 | int i, newcount = 0; | 388 | int i, newcount = 0; |
| 110 | 389 | ||
| 111 | for (i = 0; dev_list[i].handle > 0; i++) { | 390 | for (i = 0; dev_list[i].handle > 0; i++) { |
| 112 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); | 391 | if (dev_list[i].conn_type == CONNECTION_TYPE_USB) { |
| 113 | newlist[newcount++] = strdup(dev_list[i].uuid); | 392 | newlist = realloc(*devices, sizeof(char*) * (newcount+1)); |
| 114 | *devices = newlist; | 393 | newlist[newcount++] = strdup(dev_list[i].udid); |
| 394 | *devices = newlist; | ||
| 395 | } | ||
| 115 | } | 396 | } |
| 116 | usbmuxd_device_list_free(&dev_list); | 397 | usbmuxd_device_list_free(&dev_list); |
| 117 | 398 | ||
| @@ -123,62 +404,101 @@ idevice_error_t idevice_get_device_list(char ***devices, int *count) | |||
| 123 | return IDEVICE_E_SUCCESS; | 404 | return IDEVICE_E_SUCCESS; |
| 124 | } | 405 | } |
| 125 | 406 | ||
| 126 | /** | ||
| 127 | * Free a list of device uuids. | ||
| 128 | * | ||
| 129 | * @param devices List of uuids to free. | ||
| 130 | * | ||
| 131 | * @return Always returnes IDEVICE_E_SUCCESS. | ||
| 132 | */ | ||
| 133 | idevice_error_t idevice_device_list_free(char **devices) | 407 | idevice_error_t idevice_device_list_free(char **devices) |
| 134 | { | 408 | { |
| 135 | if (devices) { | 409 | if (devices) { |
| 136 | int i = 0; | 410 | int i = 0; |
| 137 | while (devices[i++]) { | 411 | while (devices[i]) { |
| 138 | free(devices[i]); | 412 | free(devices[i]); |
| 413 | i++; | ||
| 139 | } | 414 | } |
| 140 | free(devices); | 415 | free(devices); |
| 141 | } | 416 | } |
| 142 | return IDEVICE_E_SUCCESS; | 417 | return IDEVICE_E_SUCCESS; |
| 143 | } | 418 | } |
| 144 | 419 | ||
| 145 | /** | 420 | void idevice_set_debug_level(int level) |
| 146 | * Creates an idevice_t structure for the device specified by uuid, | 421 | { |
| 147 | * if the device is available. | 422 | internal_set_debug_level(level); |
| 148 | * | 423 | } |
| 149 | * @note The resulting idevice_t structure has to be freed with | 424 | |
| 150 | * idevice_free() if it is no longer used. | 425 | static idevice_t idevice_from_mux_device(usbmuxd_device_info_t *muxdev) |
| 151 | * | 426 | { |
| 152 | * @param device Upon calling this function, a pointer to a location of type | 427 | if (!muxdev) |
| 153 | * idevice_t. On successful return, this location will be populated. | 428 | return NULL; |
| 154 | * @param uuid The UUID to match. | 429 | |
| 155 | * | 430 | idevice_t device = (idevice_t)malloc(sizeof(struct idevice_private)); |
| 156 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | 431 | if (!device) |
| 157 | */ | 432 | return NULL; |
| 158 | idevice_error_t idevice_new(idevice_t * device, const char *uuid) | 433 | |
| 434 | device->udid = strdup(muxdev->udid); | ||
| 435 | device->mux_id = muxdev->handle; | ||
| 436 | device->version = 0; | ||
| 437 | device->device_class = 0; | ||
| 438 | switch (muxdev->conn_type) { | ||
| 439 | case CONNECTION_TYPE_USB: | ||
| 440 | device->conn_type = CONNECTION_USBMUXD; | ||
| 441 | device->conn_data = NULL; | ||
| 442 | break; | ||
| 443 | case CONNECTION_TYPE_NETWORK: | ||
| 444 | device->conn_type = CONNECTION_NETWORK; | ||
| 445 | struct sockaddr* saddr = (struct sockaddr*)(muxdev->conn_data); | ||
| 446 | size_t addrlen = 0; | ||
| 447 | switch (saddr->sa_family) { | ||
| 448 | case AF_INET: | ||
| 449 | addrlen = sizeof(struct sockaddr_in); | ||
| 450 | break; | ||
| 451 | #ifdef AF_INET6 | ||
| 452 | case AF_INET6: | ||
| 453 | addrlen = sizeof(struct sockaddr_in6); | ||
| 454 | break; | ||
| 455 | #endif | ||
| 456 | default: | ||
| 457 | debug_info("Unsupported address family 0x%02x\n", saddr->sa_family); | ||
| 458 | free(device->udid); | ||
| 459 | free(device); | ||
| 460 | return NULL; | ||
| 461 | } | ||
| 462 | device->conn_data = malloc(addrlen); | ||
| 463 | memcpy(device->conn_data, muxdev->conn_data, addrlen); | ||
| 464 | break; | ||
| 465 | default: | ||
| 466 | device->conn_type = 0; | ||
| 467 | device->conn_data = NULL; | ||
| 468 | break; | ||
| 469 | } | ||
| 470 | return device; | ||
| 471 | } | ||
| 472 | |||
| 473 | idevice_error_t idevice_new_with_options(idevice_t * device, const char *udid, enum idevice_options options) | ||
| 159 | { | 474 | { |
| 160 | usbmuxd_device_info_t muxdev; | 475 | usbmuxd_device_info_t muxdev; |
| 161 | int res = usbmuxd_get_device_by_uuid(uuid, &muxdev); | 476 | int usbmux_options = 0; |
| 477 | if (options & IDEVICE_LOOKUP_USBMUX) { | ||
| 478 | usbmux_options |= DEVICE_LOOKUP_USBMUX; | ||
| 479 | } | ||
| 480 | if (options & IDEVICE_LOOKUP_NETWORK) { | ||
| 481 | usbmux_options |= DEVICE_LOOKUP_NETWORK; | ||
| 482 | } | ||
| 483 | if (options & IDEVICE_LOOKUP_PREFER_NETWORK) { | ||
| 484 | usbmux_options |= DEVICE_LOOKUP_PREFER_NETWORK; | ||
| 485 | } | ||
| 486 | int res = usbmuxd_get_device(udid, &muxdev, usbmux_options); | ||
| 162 | if (res > 0) { | 487 | if (res > 0) { |
| 163 | idevice_t phone = (idevice_t) malloc(sizeof(struct idevice_private)); | 488 | *device = idevice_from_mux_device(&muxdev); |
| 164 | phone->uuid = strdup(muxdev.uuid); | 489 | if (!*device) { |
| 165 | phone->conn_type = CONNECTION_USBMUXD; | 490 | return IDEVICE_E_UNKNOWN_ERROR; |
| 166 | phone->conn_data = (void*)(long)muxdev.handle; | 491 | } |
| 167 | *device = phone; | ||
| 168 | return IDEVICE_E_SUCCESS; | 492 | return IDEVICE_E_SUCCESS; |
| 169 | } | 493 | } |
| 170 | /* other connection types could follow here */ | ||
| 171 | |||
| 172 | return IDEVICE_E_NO_DEVICE; | 494 | return IDEVICE_E_NO_DEVICE; |
| 173 | } | 495 | } |
| 174 | 496 | ||
| 175 | /** | 497 | idevice_error_t idevice_new(idevice_t * device, const char *udid) |
| 176 | * Cleans up an idevice structure, then frees the structure itself. | 498 | { |
| 177 | * This is a library-level function; deals directly with the device to tear | 499 | return idevice_new_with_options(device, udid, 0); |
| 178 | * down relations, but otherwise is mostly internal. | 500 | } |
| 179 | * | 501 | |
| 180 | * @param device idevice_t to free. | ||
| 181 | */ | ||
| 182 | idevice_error_t idevice_free(idevice_t device) | 502 | idevice_error_t idevice_free(idevice_t device) |
| 183 | { | 503 | { |
| 184 | if (!device) | 504 | if (!device) |
| @@ -187,11 +507,8 @@ idevice_error_t idevice_free(idevice_t device) | |||
| 187 | 507 | ||
| 188 | ret = IDEVICE_E_SUCCESS; | 508 | ret = IDEVICE_E_SUCCESS; |
| 189 | 509 | ||
| 190 | free(device->uuid); | 510 | free(device->udid); |
| 191 | 511 | ||
| 192 | if (device->conn_type == CONNECTION_USBMUXD) { | ||
| 193 | device->conn_data = 0; | ||
| 194 | } | ||
| 195 | if (device->conn_data) { | 512 | if (device->conn_data) { |
| 196 | free(device->conn_data); | 513 | free(device->conn_data); |
| 197 | } | 514 | } |
| @@ -199,16 +516,6 @@ idevice_error_t idevice_free(idevice_t device) | |||
| 199 | return ret; | 516 | return ret; |
| 200 | } | 517 | } |
| 201 | 518 | ||
| 202 | /** | ||
| 203 | * Set up a connection to the given device. | ||
| 204 | * | ||
| 205 | * @param device The device to connect to. | ||
| 206 | * @param port The destination port to connect to. | ||
| 207 | * @param connection Pointer to an idevice_connection_t that will be filled | ||
| 208 | * with the necessary data of the connection. | ||
| 209 | * | ||
| 210 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 211 | */ | ||
| 212 | idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) | 519 | idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connection_t *connection) |
| 213 | { | 520 | { |
| 214 | if (!device) { | 521 | if (!device) { |
| @@ -216,31 +523,80 @@ idevice_error_t idevice_connect(idevice_t device, uint16_t port, idevice_connect | |||
| 216 | } | 523 | } |
| 217 | 524 | ||
| 218 | if (device->conn_type == CONNECTION_USBMUXD) { | 525 | if (device->conn_type == CONNECTION_USBMUXD) { |
| 219 | int sfd = usbmuxd_connect((uint32_t)(long)device->conn_data, port); | 526 | int sfd = usbmuxd_connect(device->mux_id, port); |
| 220 | if (sfd < 0) { | 527 | if (sfd < 0) { |
| 221 | debug_info("ERROR: Connecting to usbmuxd failed: %d (%s)", sfd, strerror(-sfd)); | 528 | debug_info("ERROR: Connecting to usbmux device failed: %d (%s)", sfd, strerror(-sfd)); |
| 529 | switch (-sfd) { | ||
| 530 | case ECONNREFUSED: | ||
| 531 | return IDEVICE_E_CONNREFUSED; | ||
| 532 | case ENODEV: | ||
| 533 | return IDEVICE_E_NO_DEVICE; | ||
| 534 | default: | ||
| 535 | break; | ||
| 536 | } | ||
| 222 | return IDEVICE_E_UNKNOWN_ERROR; | 537 | return IDEVICE_E_UNKNOWN_ERROR; |
| 223 | } | 538 | } |
| 224 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); | 539 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); |
| 225 | new_connection->type = CONNECTION_USBMUXD; | 540 | new_connection->type = CONNECTION_USBMUXD; |
| 226 | new_connection->data = (void*)(long)sfd; | 541 | new_connection->data = (void*)(uintptr_t)sfd; |
| 227 | new_connection->ssl_data = NULL; | 542 | new_connection->ssl_data = NULL; |
| 543 | new_connection->device = device; | ||
| 544 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
| 545 | new_connection->status = IDEVICE_E_SUCCESS; | ||
| 228 | *connection = new_connection; | 546 | *connection = new_connection; |
| 229 | return IDEVICE_E_SUCCESS; | 547 | return IDEVICE_E_SUCCESS; |
| 230 | } else { | 548 | } |
| 231 | debug_info("Unknown connection type %d", device->conn_type); | 549 | if (device->conn_type == CONNECTION_NETWORK) { |
| 550 | struct sockaddr* saddr = (struct sockaddr*)(device->conn_data); | ||
| 551 | switch (saddr->sa_family) { | ||
| 552 | case AF_INET: | ||
| 553 | #ifdef AF_INET6 | ||
| 554 | case AF_INET6: | ||
| 555 | #endif | ||
| 556 | break; | ||
| 557 | default: | ||
| 558 | debug_info("Unsupported address family 0x%02x", saddr->sa_family); | ||
| 559 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 560 | } | ||
| 561 | |||
| 562 | char addrtxt[48]; | ||
| 563 | addrtxt[0] = '\0'; | ||
| 564 | |||
| 565 | if (!socket_addr_to_string(saddr, addrtxt, sizeof(addrtxt))) { | ||
| 566 | debug_info("Failed to convert network address: %d (%s)", errno, strerror(errno)); | ||
| 567 | } | ||
| 568 | |||
| 569 | debug_info("Connecting to %s port %d...", addrtxt, port); | ||
| 570 | |||
| 571 | int sfd = socket_connect_addr(saddr, port); | ||
| 572 | if (sfd < 0) { | ||
| 573 | int result = errno; | ||
| 574 | debug_info("ERROR: Connecting to network device failed: %d (%s)", result, strerror(result)); | ||
| 575 | switch (result) { | ||
| 576 | case ECONNREFUSED: | ||
| 577 | return IDEVICE_E_CONNREFUSED; | ||
| 578 | default: | ||
| 579 | break; | ||
| 580 | } | ||
| 581 | return IDEVICE_E_NO_DEVICE; | ||
| 582 | } | ||
| 583 | |||
| 584 | idevice_connection_t new_connection = (idevice_connection_t)malloc(sizeof(struct idevice_connection_private)); | ||
| 585 | new_connection->type = CONNECTION_NETWORK; | ||
| 586 | new_connection->data = (void*)(uintptr_t)sfd; | ||
| 587 | new_connection->ssl_data = NULL; | ||
| 588 | new_connection->device = device; | ||
| 589 | new_connection->ssl_recv_timeout = (unsigned int)-1; | ||
| 590 | |||
| 591 | *connection = new_connection; | ||
| 592 | |||
| 593 | return IDEVICE_E_SUCCESS; | ||
| 232 | } | 594 | } |
| 233 | 595 | ||
| 596 | debug_info("Unknown connection type %d", device->conn_type); | ||
| 234 | return IDEVICE_E_UNKNOWN_ERROR; | 597 | return IDEVICE_E_UNKNOWN_ERROR; |
| 235 | } | 598 | } |
| 236 | 599 | ||
| 237 | /** | ||
| 238 | * Disconnect from the device and clean up the connection structure. | ||
| 239 | * | ||
| 240 | * @param connection The connection to close. | ||
| 241 | * | ||
| 242 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 243 | */ | ||
| 244 | idevice_error_t idevice_disconnect(idevice_connection_t connection) | 600 | idevice_error_t idevice_disconnect(idevice_connection_t connection) |
| 245 | { | 601 | { |
| 246 | if (!connection) { | 602 | if (!connection) { |
| @@ -252,12 +608,20 @@ idevice_error_t idevice_disconnect(idevice_connection_t connection) | |||
| 252 | } | 608 | } |
| 253 | idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; | 609 | idevice_error_t result = IDEVICE_E_UNKNOWN_ERROR; |
| 254 | if (connection->type == CONNECTION_USBMUXD) { | 610 | if (connection->type == CONNECTION_USBMUXD) { |
| 255 | usbmuxd_disconnect((int)(long)connection->data); | 611 | usbmuxd_disconnect((int)(uintptr_t)connection->data); |
| 612 | connection->data = NULL; | ||
| 613 | result = IDEVICE_E_SUCCESS; | ||
| 614 | } else if (connection->type == CONNECTION_NETWORK) { | ||
| 615 | socket_close((int)(uintptr_t)connection->data); | ||
| 616 | connection->data = NULL; | ||
| 256 | result = IDEVICE_E_SUCCESS; | 617 | result = IDEVICE_E_SUCCESS; |
| 257 | } else { | 618 | } else { |
| 258 | debug_info("Unknown connection type %d", connection->type); | 619 | debug_info("Unknown connection type %d", connection->type); |
| 259 | } | 620 | } |
| 621 | |||
| 260 | free(connection); | 622 | free(connection); |
| 623 | connection = NULL; | ||
| 624 | |||
| 261 | return result; | 625 | return result; |
| 262 | } | 626 | } |
| 263 | 627 | ||
| @@ -271,46 +635,111 @@ static idevice_error_t internal_connection_send(idevice_connection_t connection, | |||
| 271 | } | 635 | } |
| 272 | 636 | ||
| 273 | if (connection->type == CONNECTION_USBMUXD) { | 637 | if (connection->type == CONNECTION_USBMUXD) { |
| 274 | int res = usbmuxd_send((int)(long)connection->data, data, len, sent_bytes); | 638 | int res; |
| 639 | do { | ||
| 640 | res = usbmuxd_send((int)(uintptr_t)connection->data, data, len, sent_bytes); | ||
| 641 | } while (res == -EAGAIN); | ||
| 275 | if (res < 0) { | 642 | if (res < 0) { |
| 276 | debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); | 643 | debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); |
| 277 | return IDEVICE_E_UNKNOWN_ERROR; | 644 | return IDEVICE_E_UNKNOWN_ERROR; |
| 278 | } | 645 | } |
| 279 | return IDEVICE_E_SUCCESS; | 646 | return IDEVICE_E_SUCCESS; |
| 280 | } else { | ||
| 281 | debug_info("Unknown connection type %d", connection->type); | ||
| 282 | } | 647 | } |
| 648 | if (connection->type == CONNECTION_NETWORK) { | ||
| 649 | int s = socket_send((int)(uintptr_t)connection->data, (void*)data, len); | ||
| 650 | if (s < 0) { | ||
| 651 | *sent_bytes = 0; | ||
| 652 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 653 | } | ||
| 654 | *sent_bytes = s; | ||
| 655 | return IDEVICE_E_SUCCESS; | ||
| 656 | } | ||
| 657 | |||
| 658 | debug_info("Unknown connection type %d", connection->type); | ||
| 283 | return IDEVICE_E_UNKNOWN_ERROR; | 659 | return IDEVICE_E_UNKNOWN_ERROR; |
| 284 | 660 | ||
| 285 | } | 661 | } |
| 286 | 662 | ||
| 287 | /** | ||
| 288 | * Send data to a device via the given connection. | ||
| 289 | * | ||
| 290 | * @param connection The connection to send data over. | ||
| 291 | * @param data Buffer with data to send. | ||
| 292 | * @param len Size of the buffer to send. | ||
| 293 | * @param sent_bytes Pointer to an uint32_t that will be filled | ||
| 294 | * with the number of bytes actually sent. | ||
| 295 | * | ||
| 296 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 297 | */ | ||
| 298 | idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) | 663 | idevice_error_t idevice_connection_send(idevice_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) |
| 299 | { | 664 | { |
| 300 | if (!connection || !data || (connection->ssl_data && !connection->ssl_data->session)) { | 665 | if (!connection || !data |
| 666 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
| 667 | || (connection->ssl_data && !connection->ssl_data->session) | ||
| 668 | #endif | ||
| 669 | ) { | ||
| 301 | return IDEVICE_E_INVALID_ARG; | 670 | return IDEVICE_E_INVALID_ARG; |
| 302 | } | 671 | } |
| 303 | 672 | ||
| 304 | if (connection->ssl_data) { | 673 | if (connection->ssl_data) { |
| 305 | ssize_t sent = gnutls_record_send(connection->ssl_data->session, (void*)data, (size_t)len); | 674 | connection->status = IDEVICE_E_SUCCESS; |
| 306 | if ((uint32_t)sent == (uint32_t)len) { | 675 | uint32_t sent = 0; |
| 307 | *sent_bytes = sent; | 676 | while (sent < len) { |
| 308 | return IDEVICE_E_SUCCESS; | 677 | #if defined(HAVE_OPENSSL) |
| 678 | int s = SSL_write(connection->ssl_data->session, (const void*)(data+sent), (int)(len-sent)); | ||
| 679 | if (s <= 0) { | ||
| 680 | int sslerr = SSL_get_error(connection->ssl_data->session, s); | ||
| 681 | if (sslerr == SSL_ERROR_WANT_WRITE) { | ||
| 682 | continue; | ||
| 683 | } | ||
| 684 | break; | ||
| 685 | } | ||
| 686 | #elif defined(HAVE_GNUTLS) | ||
| 687 | ssize_t s = gnutls_record_send(connection->ssl_data->session, (void*)(data+sent), (size_t)(len-sent)); | ||
| 688 | #elif defined(HAVE_MBEDTLS) | ||
| 689 | int s = mbedtls_ssl_write(&connection->ssl_data->ctx, (const unsigned char*)(data+sent), (size_t)(len-sent)); | ||
| 690 | #endif | ||
| 691 | if (s < 0) { | ||
| 692 | break; | ||
| 693 | } | ||
| 694 | sent += s; | ||
| 695 | } | ||
| 696 | debug_info("SSL_write %d, sent %d", len, sent); | ||
| 697 | if (sent < len) { | ||
| 698 | *sent_bytes = 0; | ||
| 699 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; | ||
| 700 | } | ||
| 701 | *sent_bytes = sent; | ||
| 702 | return IDEVICE_E_SUCCESS; | ||
| 703 | } | ||
| 704 | uint32_t sent = 0; | ||
| 705 | while (sent < len) { | ||
| 706 | uint32_t bytes = 0; | ||
| 707 | int s = internal_connection_send(connection, data+sent, len-sent, &bytes); | ||
| 708 | if (s < 0) { | ||
| 709 | break; | ||
| 710 | } | ||
| 711 | sent += bytes; | ||
| 712 | } | ||
| 713 | debug_info("internal_connection_send %d, sent %d", len, sent); | ||
| 714 | if (sent < len) { | ||
| 715 | *sent_bytes = sent; | ||
| 716 | if (sent == 0) { | ||
| 717 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 718 | } | ||
| 719 | return IDEVICE_E_NOT_ENOUGH_DATA; | ||
| 720 | } | ||
| 721 | *sent_bytes = sent; | ||
| 722 | return IDEVICE_E_SUCCESS; | ||
| 723 | } | ||
| 724 | |||
| 725 | static inline idevice_error_t socket_recv_to_idevice_error(int conn_error, uint32_t len, uint32_t received) | ||
| 726 | { | ||
| 727 | if (conn_error < 0) { | ||
| 728 | switch (conn_error) { | ||
| 729 | case -EAGAIN: | ||
| 730 | if (len) { | ||
| 731 | debug_info("ERROR: received partial data %d/%d (%s)", received, len, strerror(-conn_error)); | ||
| 732 | } else { | ||
| 733 | debug_info("ERROR: received partial data (%s)", strerror(-conn_error)); | ||
| 734 | } | ||
| 735 | return IDEVICE_E_NOT_ENOUGH_DATA; | ||
| 736 | case -ETIMEDOUT: | ||
| 737 | return IDEVICE_E_TIMEOUT; | ||
| 738 | default: | ||
| 739 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 309 | } | 740 | } |
| 310 | *sent_bytes = 0; | ||
| 311 | return IDEVICE_E_SSL_ERROR; | ||
| 312 | } | 741 | } |
| 313 | return internal_connection_send(connection, data, len, sent_bytes); | 742 | return IDEVICE_E_SUCCESS; |
| 314 | } | 743 | } |
| 315 | 744 | ||
| 316 | /** | 745 | /** |
| @@ -324,47 +753,92 @@ static idevice_error_t internal_connection_receive_timeout(idevice_connection_t | |||
| 324 | } | 753 | } |
| 325 | 754 | ||
| 326 | if (connection->type == CONNECTION_USBMUXD) { | 755 | if (connection->type == CONNECTION_USBMUXD) { |
| 327 | int res = usbmuxd_recv_timeout((int)(long)connection->data, data, len, recv_bytes, timeout); | 756 | int conn_error = usbmuxd_recv_timeout((int)(uintptr_t)connection->data, data, len, recv_bytes, timeout); |
| 328 | if (res < 0) { | 757 | idevice_error_t error = socket_recv_to_idevice_error(conn_error, len, *recv_bytes); |
| 329 | debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", res, strerror(-res)); | 758 | if (error == IDEVICE_E_UNKNOWN_ERROR) { |
| 330 | return IDEVICE_E_UNKNOWN_ERROR; | 759 | debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", conn_error, strerror(-conn_error)); |
| 331 | } | 760 | } |
| 332 | return IDEVICE_E_SUCCESS; | 761 | return error; |
| 333 | } else { | 762 | } |
| 334 | debug_info("Unknown connection type %d", connection->type); | 763 | if (connection->type == CONNECTION_NETWORK) { |
| 764 | int res = socket_receive_timeout((int)(uintptr_t)connection->data, data, len, 0, timeout); | ||
| 765 | idevice_error_t error = socket_recv_to_idevice_error(res, 0, 0); | ||
| 766 | if (error == IDEVICE_E_SUCCESS) { | ||
| 767 | *recv_bytes = (uint32_t)res; | ||
| 768 | } else if (error == IDEVICE_E_UNKNOWN_ERROR) { | ||
| 769 | debug_info("ERROR: socket_receive_timeout returned %d (%s)", res, strerror(-res)); | ||
| 770 | } | ||
| 771 | return error; | ||
| 335 | } | 772 | } |
| 773 | |||
| 774 | debug_info("Unknown connection type %d", connection->type); | ||
| 336 | return IDEVICE_E_UNKNOWN_ERROR; | 775 | return IDEVICE_E_UNKNOWN_ERROR; |
| 337 | } | 776 | } |
| 338 | 777 | ||
| 339 | /** | ||
| 340 | * Receive data from a device via the given connection. | ||
| 341 | * This function will return after the given timeout even if no data has been | ||
| 342 | * received. | ||
| 343 | * | ||
| 344 | * @param connection The connection to receive data from. | ||
| 345 | * @param data Buffer that will be filled with the received data. | ||
| 346 | * This buffer has to be large enough to hold len bytes. | ||
| 347 | * @param len Buffer size or number of bytes to receive. | ||
| 348 | * @param recv_bytes Number of bytes actually received. | ||
| 349 | * @param timeout Timeout in milliseconds after which this function should | ||
| 350 | * return even if no data has been received. | ||
| 351 | * | ||
| 352 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 353 | */ | ||
| 354 | idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) | 778 | idevice_error_t idevice_connection_receive_timeout(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) |
| 355 | { | 779 | { |
| 356 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | 780 | if (!connection |
| 781 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
| 782 | || (connection->ssl_data && !connection->ssl_data->session) | ||
| 783 | #endif | ||
| 784 | || len == 0 | ||
| 785 | ) { | ||
| 357 | return IDEVICE_E_INVALID_ARG; | 786 | return IDEVICE_E_INVALID_ARG; |
| 358 | } | 787 | } |
| 359 | 788 | ||
| 360 | if (connection->ssl_data) { | 789 | if (connection->ssl_data) { |
| 361 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | 790 | uint32_t received = 0; |
| 362 | if (received > 0) { | 791 | |
| 792 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
| 793 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
| 794 | } | ||
| 795 | |||
| 796 | // this should be reset after the SSL_read call on all codepaths, as | ||
| 797 | // the supplied timeout should only apply to the current read. | ||
| 798 | connection->ssl_recv_timeout = timeout; | ||
| 799 | connection->status = IDEVICE_E_SUCCESS; | ||
| 800 | while (received < len) { | ||
| 801 | #if defined(HAVE_OPENSSL) | ||
| 802 | int r = SSL_read(connection->ssl_data->session, (void*)((char*)(data+received)), (int)len-received); | ||
| 803 | if (r > 0) { | ||
| 804 | received += r; | ||
| 805 | } else { | ||
| 806 | int sslerr = SSL_get_error(connection->ssl_data->session, r); | ||
| 807 | if (sslerr == SSL_ERROR_WANT_READ) { | ||
| 808 | continue; | ||
| 809 | } else if (sslerr == SSL_ERROR_ZERO_RETURN) { | ||
| 810 | if (connection->status == IDEVICE_E_TIMEOUT) { | ||
| 811 | SSL_set_shutdown(connection->ssl_data->session, 0); | ||
| 812 | } | ||
| 813 | } | ||
| 814 | break; | ||
| 815 | } | ||
| 816 | #elif defined(HAVE_GNUTLS) | ||
| 817 | ssize_t r = gnutls_record_recv(connection->ssl_data->session, (void*)(data+received), (size_t)len-received); | ||
| 818 | if (r > 0) { | ||
| 819 | received += r; | ||
| 820 | } else { | ||
| 821 | break; | ||
| 822 | } | ||
| 823 | #elif defined(HAVE_MBEDTLS) | ||
| 824 | int r = mbedtls_ssl_read(&connection->ssl_data->ctx, (void*)(data+received), (size_t)len-received); | ||
| 825 | if (r > 0) { | ||
| 826 | received += r; | ||
| 827 | } else { | ||
| 828 | break; | ||
| 829 | } | ||
| 830 | #endif | ||
| 831 | } | ||
| 832 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
| 833 | |||
| 834 | debug_info("SSL_read %d, received %d", len, received); | ||
| 835 | if (received < len) { | ||
| 363 | *recv_bytes = received; | 836 | *recv_bytes = received; |
| 364 | return IDEVICE_E_SUCCESS; | 837 | return connection->status == IDEVICE_E_SUCCESS ? IDEVICE_E_SSL_ERROR : connection->status; |
| 365 | } | 838 | } |
| 366 | *recv_bytes = 0; | 839 | |
| 367 | return IDEVICE_E_SSL_ERROR; | 840 | *recv_bytes = received; |
| 841 | return IDEVICE_E_SUCCESS; | ||
| 368 | } | 842 | } |
| 369 | return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); | 843 | return internal_connection_receive_timeout(connection, data, len, recv_bytes, timeout); |
| 370 | } | 844 | } |
| @@ -379,40 +853,50 @@ static idevice_error_t internal_connection_receive(idevice_connection_t connecti | |||
| 379 | } | 853 | } |
| 380 | 854 | ||
| 381 | if (connection->type == CONNECTION_USBMUXD) { | 855 | if (connection->type == CONNECTION_USBMUXD) { |
| 382 | int res = usbmuxd_recv((int)(long)connection->data, data, len, recv_bytes); | 856 | int res = usbmuxd_recv((int)(uintptr_t)connection->data, data, len, recv_bytes); |
| 383 | if (res < 0) { | 857 | if (res < 0) { |
| 384 | debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); | 858 | debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); |
| 385 | return IDEVICE_E_UNKNOWN_ERROR; | 859 | return IDEVICE_E_UNKNOWN_ERROR; |
| 386 | } | 860 | } |
| 387 | |||
| 388 | return IDEVICE_E_SUCCESS; | 861 | return IDEVICE_E_SUCCESS; |
| 389 | } else { | ||
| 390 | debug_info("Unknown connection type %d", connection->type); | ||
| 391 | } | 862 | } |
| 863 | if (connection->type == CONNECTION_NETWORK) { | ||
| 864 | int res = socket_receive((int)(uintptr_t)connection->data, data, len); | ||
| 865 | if (res < 0) { | ||
| 866 | debug_info("ERROR: socket_receive returned %d (%s)", res, strerror(-res)); | ||
| 867 | return IDEVICE_E_UNKNOWN_ERROR; | ||
| 868 | } | ||
| 869 | *recv_bytes = (uint32_t)res; | ||
| 870 | return IDEVICE_E_SUCCESS; | ||
| 871 | } | ||
| 872 | |||
| 873 | debug_info("Unknown connection type %d", connection->type); | ||
| 392 | return IDEVICE_E_UNKNOWN_ERROR; | 874 | return IDEVICE_E_UNKNOWN_ERROR; |
| 393 | } | 875 | } |
| 394 | 876 | ||
| 395 | /** | ||
| 396 | * Receive data from a device via the given connection. | ||
| 397 | * This function is like idevice_connection_receive_timeout, but with a | ||
| 398 | * predefined reasonable timeout. | ||
| 399 | * | ||
| 400 | * @param connection The connection to receive data from. | ||
| 401 | * @param data Buffer that will be filled with the received data. | ||
| 402 | * This buffer has to be large enough to hold len bytes. | ||
| 403 | * @param len Buffer size or number of bytes to receive. | ||
| 404 | * @param recv_bytes Number of bytes actually received. | ||
| 405 | * | ||
| 406 | * @return IDEVICE_E_SUCCESS if ok, otherwise an error code. | ||
| 407 | */ | ||
| 408 | idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) | 877 | idevice_error_t idevice_connection_receive(idevice_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) |
| 409 | { | 878 | { |
| 410 | if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { | 879 | if (!connection |
| 880 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
| 881 | || (connection->ssl_data && !connection->ssl_data->session) | ||
| 882 | #endif | ||
| 883 | ) { | ||
| 411 | return IDEVICE_E_INVALID_ARG; | 884 | return IDEVICE_E_INVALID_ARG; |
| 412 | } | 885 | } |
| 413 | 886 | ||
| 414 | if (connection->ssl_data) { | 887 | if (connection->ssl_data) { |
| 888 | if (connection->ssl_recv_timeout != (unsigned int)-1) { | ||
| 889 | debug_info("WARNING: ssl_recv_timeout was not properly reset in idevice_connection_receive_timeout"); | ||
| 890 | connection->ssl_recv_timeout = (unsigned int)-1; | ||
| 891 | } | ||
| 892 | #if defined(HAVE_OPENSSL) | ||
| 893 | int received = SSL_read(connection->ssl_data->session, (void*)data, (int)len); | ||
| 894 | debug_info("SSL_read %d, received %d", len, received); | ||
| 895 | #elif defined(HAVE_GNUTLS) | ||
| 415 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); | 896 | ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); |
| 897 | #elif defined(HAVE_MBEDTLS) | ||
| 898 | int received = mbedtls_ssl_read(&connection->ssl_data->ctx, (unsigned char*)data, (size_t)len); | ||
| 899 | #endif | ||
| 416 | if (received > 0) { | 900 | if (received > 0) { |
| 417 | *recv_bytes = received; | 901 | *recv_bytes = received; |
| 418 | return IDEVICE_E_SUCCESS; | 902 | return IDEVICE_E_SUCCESS; |
| @@ -423,89 +907,119 @@ idevice_error_t idevice_connection_receive(idevice_connection_t connection, char | |||
| 423 | return internal_connection_receive(connection, data, len, recv_bytes); | 907 | return internal_connection_receive(connection, data, len, recv_bytes); |
| 424 | } | 908 | } |
| 425 | 909 | ||
| 426 | /** | 910 | idevice_error_t idevice_connection_get_fd(idevice_connection_t connection, int *fd) |
| 427 | * Gets the handle of the device. Depends on the connection type. | ||
| 428 | */ | ||
| 429 | idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) | ||
| 430 | { | 911 | { |
| 431 | if (!device) | 912 | if (!connection || !fd) { |
| 432 | return IDEVICE_E_INVALID_ARG; | 913 | return IDEVICE_E_INVALID_ARG; |
| 914 | } | ||
| 433 | 915 | ||
| 434 | if (device->conn_type == CONNECTION_USBMUXD) { | 916 | if (connection->type == CONNECTION_USBMUXD) { |
| 435 | *handle = (uint32_t)(long)device->conn_data; | 917 | *fd = (int)(uintptr_t)connection->data; |
| 436 | return IDEVICE_E_SUCCESS; | 918 | return IDEVICE_E_SUCCESS; |
| 437 | } else { | ||
| 438 | debug_info("Unknown connection type %d", device->conn_type); | ||
| 439 | } | 919 | } |
| 920 | if (connection->type == CONNECTION_NETWORK) { | ||
| 921 | *fd = (int)(uintptr_t)connection->data; | ||
| 922 | return IDEVICE_E_SUCCESS; | ||
| 923 | } | ||
| 924 | |||
| 925 | debug_info("Unknown connection type %d", connection->type); | ||
| 440 | return IDEVICE_E_UNKNOWN_ERROR; | 926 | return IDEVICE_E_UNKNOWN_ERROR; |
| 441 | } | 927 | } |
| 442 | 928 | ||
| 443 | /** | 929 | idevice_error_t idevice_get_handle(idevice_t device, uint32_t *handle) |
| 444 | * Gets the unique id for the device. | 930 | { |
| 445 | */ | 931 | if (!device || !handle) |
| 446 | idevice_error_t idevice_get_uuid(idevice_t device, char **uuid) | 932 | return IDEVICE_E_INVALID_ARG; |
| 933 | |||
| 934 | *handle = device->mux_id; | ||
| 935 | return IDEVICE_E_SUCCESS; | ||
| 936 | } | ||
| 937 | |||
| 938 | idevice_error_t idevice_get_udid(idevice_t device, char **udid) | ||
| 447 | { | 939 | { |
| 448 | if (!device || !uuid) | 940 | if (!device || !udid) |
| 449 | return IDEVICE_E_INVALID_ARG; | 941 | return IDEVICE_E_INVALID_ARG; |
| 450 | 942 | ||
| 451 | *uuid = strdup(device->uuid); | 943 | if (device->udid) { |
| 944 | *udid = strdup(device->udid); | ||
| 945 | } | ||
| 452 | return IDEVICE_E_SUCCESS; | 946 | return IDEVICE_E_SUCCESS; |
| 453 | } | 947 | } |
| 454 | 948 | ||
| 949 | unsigned int idevice_get_device_version(idevice_t device) | ||
| 950 | { | ||
| 951 | if (!device) { | ||
| 952 | return 0; | ||
| 953 | } | ||
| 954 | if (!device->version) { | ||
| 955 | lockdownd_client_t lockdown = NULL; | ||
| 956 | lockdownd_client_new(device, &lockdown, NULL); | ||
| 957 | // we don't handle any errors here. We should have the product version cached now. | ||
| 958 | lockdownd_client_free(lockdown); | ||
| 959 | } | ||
| 960 | return device->version; | ||
| 961 | } | ||
| 962 | |||
| 963 | #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS) | ||
| 964 | typedef ssize_t ssl_cb_ret_type_t; | ||
| 965 | #elif defined(HAVE_MBEDTLS) | ||
| 966 | typedef int ssl_cb_ret_type_t; | ||
| 967 | #endif | ||
| 968 | |||
| 455 | /** | 969 | /** |
| 456 | * Internally used gnutls callback function for receiving encrypted data. | 970 | * Internally used SSL callback function for receiving encrypted data. |
| 457 | */ | 971 | */ |
| 458 | static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 972 | static ssl_cb_ret_type_t internal_ssl_read(idevice_connection_t connection, char *buffer, size_t length) |
| 459 | { | 973 | { |
| 460 | int bytes = 0, pos_start_fill = 0; | 974 | uint32_t bytes = 0; |
| 461 | size_t tbytes = 0; | 975 | uint32_t pos = 0; |
| 462 | int this_len = length; | ||
| 463 | idevice_error_t res; | 976 | idevice_error_t res; |
| 464 | idevice_connection_t connection = (idevice_connection_t)transport; | 977 | unsigned int timeout = connection->ssl_recv_timeout; |
| 465 | char *recv_buffer; | ||
| 466 | 978 | ||
| 467 | debug_info("pre-read client wants %zi bytes", length); | 979 | debug_info("pre-read length = %zi bytes", length); |
| 468 | |||
| 469 | recv_buffer = (char *) malloc(sizeof(char) * this_len); | ||
| 470 | 980 | ||
| 471 | /* repeat until we have the full data or an error occurs */ | 981 | /* repeat until we have the full data or an error occurs */ |
| 472 | do { | 982 | do { |
| 473 | if ((res = internal_connection_receive(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IDEVICE_E_SUCCESS) { | 983 | bytes = 0; |
| 474 | debug_info("ERROR: idevice_connection_receive returned %d", res); | 984 | if (timeout == (unsigned int)-1) { |
| 475 | return res; | 985 | res = internal_connection_receive(connection, buffer + pos, (uint32_t)length - pos, &bytes); |
| 986 | } else { | ||
| 987 | res = internal_connection_receive_timeout(connection, buffer + pos, (uint32_t)length - pos, &bytes, (unsigned int)timeout); | ||
| 988 | } | ||
| 989 | if (res != IDEVICE_E_SUCCESS) { | ||
| 990 | if (res != IDEVICE_E_TIMEOUT) { | ||
| 991 | debug_info("ERROR: %s returned %d", (timeout == (unsigned int)-1) ? "internal_connection_receive" : "internal_connection_receive_timeout", res); | ||
| 992 | } | ||
| 993 | connection->status = res; | ||
| 994 | return -1; | ||
| 476 | } | 995 | } |
| 477 | debug_info("post-read we got %i bytes", bytes); | 996 | debug_info("read %i bytes", bytes); |
| 478 | 997 | ||
| 479 | /* increase read count */ | 998 | /* increase read count */ |
| 480 | tbytes += bytes; | 999 | pos += bytes; |
| 481 | 1000 | if (pos < (uint32_t)length) { | |
| 482 | /* fill the buffer with what we got right now */ | 1001 | debug_info("re-read trying to read missing %i bytes", (uint32_t)length - pos); |
| 483 | memcpy(buffer + pos_start_fill, recv_buffer, bytes); | ||
| 484 | pos_start_fill += bytes; | ||
| 485 | |||
| 486 | if (tbytes >= length) { | ||
| 487 | break; | ||
| 488 | } | 1002 | } |
| 1003 | } while (pos < (uint32_t)length); | ||
| 489 | 1004 | ||
| 490 | this_len = length - tbytes; | 1005 | debug_info("post-read received %i bytes", pos); |
| 491 | debug_info("re-read trying to read missing %i bytes", this_len); | ||
| 492 | } while (tbytes < length); | ||
| 493 | 1006 | ||
| 494 | if (recv_buffer) { | 1007 | return pos; |
| 495 | free(recv_buffer); | ||
| 496 | } | ||
| 497 | return tbytes; | ||
| 498 | } | 1008 | } |
| 499 | 1009 | ||
| 500 | /** | 1010 | /** |
| 501 | * Internally used gnutls callback function for sending encrypted data. | 1011 | * Internally used SSL callback function for sending encrypted data. |
| 502 | */ | 1012 | */ |
| 503 | static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) | 1013 | static ssl_cb_ret_type_t internal_ssl_write(idevice_connection_t connection, const char *buffer, size_t length) |
| 504 | { | 1014 | { |
| 505 | uint32_t bytes = 0; | 1015 | uint32_t bytes = 0; |
| 506 | idevice_connection_t connection = (idevice_connection_t)transport; | 1016 | idevice_error_t res; |
| 507 | debug_info("pre-send length = %zi", length); | 1017 | debug_info("pre-send length = %zi bytes", length); |
| 508 | internal_connection_send(connection, buffer, length, &bytes); | 1018 | if ((res = internal_connection_send(connection, buffer, length, &bytes)) != IDEVICE_E_SUCCESS) { |
| 1019 | debug_info("ERROR: internal_connection_send returned %d", res); | ||
| 1020 | connection->status = res; | ||
| 1021 | return -1; | ||
| 1022 | } | ||
| 509 | debug_info("post-send sent %i bytes", bytes); | 1023 | debug_info("post-send sent %i bytes", bytes); |
| 510 | return bytes; | 1024 | return bytes; |
| 511 | } | 1025 | } |
| @@ -518,6 +1032,14 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) | |||
| 518 | if (!ssl_data) | 1032 | if (!ssl_data) |
| 519 | return; | 1033 | return; |
| 520 | 1034 | ||
| 1035 | #if defined(HAVE_OPENSSL) | ||
| 1036 | if (ssl_data->session) { | ||
| 1037 | SSL_free(ssl_data->session); | ||
| 1038 | } | ||
| 1039 | if (ssl_data->ctx) { | ||
| 1040 | SSL_CTX_free(ssl_data->ctx); | ||
| 1041 | } | ||
| 1042 | #elif defined(HAVE_GNUTLS) | ||
| 521 | if (ssl_data->session) { | 1043 | if (ssl_data->session) { |
| 522 | gnutls_deinit(ssl_data->session); | 1044 | gnutls_deinit(ssl_data->session); |
| 523 | } | 1045 | } |
| @@ -536,20 +1058,121 @@ static void internal_ssl_cleanup(ssl_data_t ssl_data) | |||
| 536 | if (ssl_data->host_privkey) { | 1058 | if (ssl_data->host_privkey) { |
| 537 | gnutls_x509_privkey_deinit(ssl_data->host_privkey); | 1059 | gnutls_x509_privkey_deinit(ssl_data->host_privkey); |
| 538 | } | 1060 | } |
| 1061 | #elif defined(HAVE_MBEDTLS) | ||
| 1062 | mbedtls_pk_free(&ssl_data->root_privkey); | ||
| 1063 | mbedtls_x509_crt_free(&ssl_data->certificate); | ||
| 1064 | mbedtls_entropy_free(&ssl_data->entropy); | ||
| 1065 | mbedtls_ctr_drbg_free(&ssl_data->ctr_drbg); | ||
| 1066 | mbedtls_ssl_config_free(&ssl_data->config); | ||
| 1067 | mbedtls_ssl_free(&ssl_data->ctx); | ||
| 1068 | #endif | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | #ifdef HAVE_OPENSSL | ||
| 1072 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
| 1073 | static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, size_t len, int argi, long argl, int retvalue, size_t *processed) | ||
| 1074 | #else | ||
| 1075 | static long ssl_idevice_bio_callback(BIO *b, int oper, const char *argp, int argi, long argl, long retvalue) | ||
| 1076 | #endif | ||
| 1077 | { | ||
| 1078 | ssize_t bytes = 0; | ||
| 1079 | idevice_connection_t conn = (idevice_connection_t)BIO_get_callback_arg(b); | ||
| 1080 | #if OPENSSL_VERSION_NUMBER < 0x30000000L | ||
| 1081 | size_t len = (size_t)argi; | ||
| 1082 | #endif | ||
| 1083 | switch (oper) { | ||
| 1084 | case (BIO_CB_READ|BIO_CB_RETURN): | ||
| 1085 | if (argp) { | ||
| 1086 | bytes = internal_ssl_read(conn, (char *)argp, len); | ||
| 1087 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
| 1088 | *processed = (size_t)(bytes < 0) ? 0 : bytes; | ||
| 1089 | #endif | ||
| 1090 | return (long)bytes; | ||
| 1091 | } | ||
| 1092 | return 0; | ||
| 1093 | case (BIO_CB_PUTS|BIO_CB_RETURN): | ||
| 1094 | len = strlen(argp); | ||
| 1095 | // fallthrough | ||
| 1096 | case (BIO_CB_WRITE|BIO_CB_RETURN): | ||
| 1097 | bytes = internal_ssl_write(conn, argp, len); | ||
| 1098 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
| 1099 | *processed = (size_t)(bytes < 0) ? 0 : bytes; | ||
| 1100 | #endif | ||
| 1101 | return (long)bytes; | ||
| 1102 | default: | ||
| 1103 | return retvalue; | ||
| 1104 | } | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | static BIO *ssl_idevice_bio_new(idevice_connection_t conn) | ||
| 1108 | { | ||
| 1109 | BIO *b = BIO_new(BIO_s_null()); | ||
| 1110 | if (!b) return NULL; | ||
| 1111 | BIO_set_callback_arg(b, (char *)conn); | ||
| 1112 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
| 1113 | BIO_set_callback_ex(b, ssl_idevice_bio_callback); | ||
| 1114 | #else | ||
| 1115 | BIO_set_callback(b, ssl_idevice_bio_callback); | ||
| 1116 | #endif | ||
| 1117 | return b; | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | static int ssl_verify_callback(int ok, X509_STORE_CTX *ctx) | ||
| 1121 | { | ||
| 1122 | return 1; | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | #ifndef STRIP_DEBUG_CODE | ||
| 1126 | static const char *ssl_error_to_string(int e) | ||
| 1127 | { | ||
| 1128 | switch(e) { | ||
| 1129 | case SSL_ERROR_NONE: | ||
| 1130 | return "SSL_ERROR_NONE"; | ||
| 1131 | case SSL_ERROR_SSL: | ||
| 1132 | return ERR_error_string(ERR_get_error(), NULL); | ||
| 1133 | case SSL_ERROR_WANT_READ: | ||
| 1134 | return "SSL_ERROR_WANT_READ"; | ||
| 1135 | case SSL_ERROR_WANT_WRITE: | ||
| 1136 | return "SSL_ERROR_WANT_WRITE"; | ||
| 1137 | case SSL_ERROR_WANT_X509_LOOKUP: | ||
| 1138 | return "SSL_ERROR_WANT_X509_LOOKUP"; | ||
| 1139 | case SSL_ERROR_SYSCALL: | ||
| 1140 | return "SSL_ERROR_SYSCALL"; | ||
| 1141 | case SSL_ERROR_ZERO_RETURN: | ||
| 1142 | return "SSL_ERROR_ZERO_RETURN"; | ||
| 1143 | case SSL_ERROR_WANT_CONNECT: | ||
| 1144 | return "SSL_ERROR_WANT_CONNECT"; | ||
| 1145 | case SSL_ERROR_WANT_ACCEPT: | ||
| 1146 | return "SSL_ERROR_WANT_ACCEPT"; | ||
| 1147 | default: | ||
| 1148 | return "UNKOWN_ERROR_VALUE"; | ||
| 1149 | } | ||
| 539 | } | 1150 | } |
| 1151 | #endif | ||
| 1152 | #endif | ||
| 540 | 1153 | ||
| 1154 | #if defined(HAVE_GNUTLS) | ||
| 541 | /** | 1155 | /** |
| 542 | * Internally used gnutls callback function that gets called during handshake. | 1156 | * Internally used gnutls callback function that gets called during handshake. |
| 543 | */ | 1157 | */ |
| 544 | static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) | 1158 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 |
| 1159 | static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr2_st * st) | ||
| 1160 | #else | ||
| 1161 | static int internal_cert_callback(gnutls_session_t session, const gnutls_datum_t * req_ca_rdn, int nreqs, const gnutls_pk_algorithm_t * sign_algos, int sign_algos_length, gnutls_retr_st * st) | ||
| 1162 | #endif | ||
| 545 | { | 1163 | { |
| 546 | int res = -1; | 1164 | int res = -1; |
| 547 | gnutls_certificate_type_t type = gnutls_certificate_type_get (session); | 1165 | gnutls_certificate_type_t type = gnutls_certificate_type_get(session); |
| 548 | if (type == GNUTLS_CRT_X509) { | 1166 | if (type == GNUTLS_CRT_X509) { |
| 549 | ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr (session); | 1167 | ssl_data_t ssl_data = (ssl_data_t)gnutls_session_get_ptr(session); |
| 550 | if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) { | 1168 | if (ssl_data && ssl_data->host_privkey && ssl_data->host_cert) { |
| 551 | debug_info("Passing certificate"); | 1169 | debug_info("Passing certificate"); |
| 1170 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 | ||
| 1171 | st->cert_type = type; | ||
| 1172 | st->key_type = GNUTLS_PRIVKEY_X509; | ||
| 1173 | #else | ||
| 552 | st->type = type; | 1174 | st->type = type; |
| 1175 | #endif | ||
| 553 | st->ncerts = 1; | 1176 | st->ncerts = 1; |
| 554 | st->cert.x509 = &ssl_data->host_cert; | 1177 | st->cert.x509 = &ssl_data->host_cert; |
| 555 | st->key.x509 = ssl_data->host_privkey; | 1178 | st->key.x509 = ssl_data->host_privkey; |
| @@ -559,46 +1182,204 @@ static int internal_cert_callback (gnutls_session_t session, const gnutls_datum_ | |||
| 559 | } | 1182 | } |
| 560 | return res; | 1183 | return res; |
| 561 | } | 1184 | } |
| 1185 | #elif defined(HAVE_MBEDTLS) | ||
| 1186 | static void _mbedtls_log_cb(void* ctx, int level, const char* filename, int line, const char* message) | ||
| 1187 | { | ||
| 1188 | fprintf(stderr, "[mbedtls][%d] %s:%d => %s", level, filename, line, message); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | static int cert_verify_cb(void* ctx, mbedtls_x509_crt* cert, int depth, uint32_t *flags) | ||
| 1192 | { | ||
| 1193 | *flags = 0; | ||
| 1194 | return 0; | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | static int _mbedtls_f_rng(void* p_rng, unsigned char* buf, size_t len) | ||
| 1198 | { | ||
| 1199 | memset(buf, 4, len); | ||
| 1200 | return 0; | ||
| 1201 | } | ||
| 1202 | #endif | ||
| 562 | 1203 | ||
| 563 | /** | ||
| 564 | * Enables SSL for the given connection. | ||
| 565 | * | ||
| 566 | * @param connection The connection to enable SSL for. | ||
| 567 | * | ||
| 568 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
| 569 | * is NULL or connection->ssl_data is non-NULL, or IDEVICE_E_SSL_ERROR when | ||
| 570 | * SSL initialization, setup, or handshake fails. | ||
| 571 | */ | ||
| 572 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | 1204 | idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) |
| 573 | { | 1205 | { |
| 574 | if (!connection || connection->ssl_data) | 1206 | if (!connection || connection->ssl_data) |
| 575 | return IDEVICE_E_INVALID_ARG; | 1207 | return IDEVICE_E_INVALID_ARG; |
| 576 | 1208 | ||
| 577 | idevice_error_t ret = IDEVICE_E_SSL_ERROR; | 1209 | idevice_error_t ret = IDEVICE_E_SSL_ERROR; |
| 578 | uint32_t return_me = 0; | 1210 | plist_t pair_record = NULL; |
| 1211 | |||
| 1212 | userpref_error_t uerr = userpref_read_pair_record(connection->device->udid, &pair_record); | ||
| 1213 | if (uerr != USERPREF_E_SUCCESS) { | ||
| 1214 | debug_info("ERROR: Failed enabling SSL. Unable to read pair record for udid %s (%d)", connection->device->udid, uerr); | ||
| 1215 | return ret; | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | #if defined(HAVE_OPENSSL) | ||
| 1219 | key_data_t root_cert = { NULL, 0 }; | ||
| 1220 | key_data_t root_privkey = { NULL, 0 }; | ||
| 1221 | |||
| 1222 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert); | ||
| 1223 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey); | ||
| 1224 | |||
| 1225 | if (pair_record) | ||
| 1226 | plist_free(pair_record); | ||
| 1227 | |||
| 1228 | BIO *ssl_bio = ssl_idevice_bio_new(connection); | ||
| 1229 | if (!ssl_bio) { | ||
| 1230 | debug_info("ERROR: Could not create SSL bio."); | ||
| 1231 | return ret; | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); | ||
| 1235 | if (ssl_ctx == NULL) { | ||
| 1236 | debug_info("ERROR: Could not create SSL context."); | ||
| 1237 | BIO_free(ssl_bio); | ||
| 1238 | return ret; | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) || \ | ||
| 1242 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3060000fL)) | ||
| 1243 | SSL_CTX_set_security_level(ssl_ctx, 0); | ||
| 1244 | #endif | ||
| 1245 | |||
| 1246 | #if OPENSSL_VERSION_NUMBER < 0x10100002L || \ | ||
| 1247 | (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2060000fL)) | ||
| 1248 | /* force use of TLSv1 for older devices */ | ||
| 1249 | if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) { | ||
| 1250 | #ifdef SSL_OP_NO_TLSv1_1 | ||
| 1251 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); | ||
| 1252 | #endif | ||
| 1253 | #ifdef SSL_OP_NO_TLSv1_2 | ||
| 1254 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2); | ||
| 1255 | #endif | ||
| 1256 | #ifdef SSL_OP_NO_TLSv1_3 | ||
| 1257 | SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); | ||
| 1258 | #endif | ||
| 1259 | } | ||
| 1260 | #else | ||
| 1261 | SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION); | ||
| 1262 | if (connection->device->version < IDEVICE_DEVICE_VERSION(10,0,0)) { | ||
| 1263 | SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION); | ||
| 1264 | if (connection->device->version == 0) { | ||
| 1265 | /* | ||
| 1266 | iOS 1 doesn't understand TLS1_VERSION, it can only speak SSL3_VERSION. | ||
| 1267 | However, modern OpenSSL is usually compiled without SSLv3 support. | ||
| 1268 | So if we set min_proto_version to SSL3_VERSION on an OpenSSL instance which doesn't support it, | ||
| 1269 | it will just ignore min_proto_version altogether and fall back to an even higher version. | ||
| 1270 | To avoid accidentally breaking iOS 2.0+, we set min version to 0 instead. | ||
| 1271 | Here is what documentation says: | ||
| 1272 | Setting the minimum or maximum version to 0, | ||
| 1273 | will enable protocol versions down to the lowest version, | ||
| 1274 | or up to the highest version supported by the library, respectively. | ||
| 1275 | */ | ||
| 1276 | SSL_CTX_set_min_proto_version(ssl_ctx, 0); | ||
| 1277 | } | ||
| 1278 | } | ||
| 1279 | #endif | ||
| 1280 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
| 1281 | #if defined(SSL_OP_IGNORE_UNEXPECTED_EOF) | ||
| 1282 | /* | ||
| 1283 | * For OpenSSL 3 and later, mark close_notify alerts as optional. | ||
| 1284 | * For prior versions of OpenSSL we check for SSL_ERROR_SYSCALL when | ||
| 1285 | * reading instead (this error changes to SSL_ERROR_SSL in OpenSSL 3). | ||
| 1286 | */ | ||
| 1287 | SSL_CTX_set_options(ssl_ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); | ||
| 1288 | #endif | ||
| 1289 | #if defined(SSL_OP_LEGACY_SERVER_CONNECT) | ||
| 1290 | /* | ||
| 1291 | * Without setting SSL_OP_LEGACY_SERVER_CONNECT, OpenSSL 3 fails with | ||
| 1292 | * error "unsafe legacy renegotiation disabled" when talking to iOS 5 | ||
| 1293 | */ | ||
| 1294 | SSL_CTX_set_options(ssl_ctx, SSL_OP_LEGACY_SERVER_CONNECT); | ||
| 1295 | #endif | ||
| 1296 | #endif | ||
| 1297 | |||
| 1298 | BIO* membp; | ||
| 1299 | X509* rootCert = NULL; | ||
| 1300 | membp = BIO_new_mem_buf(root_cert.data, root_cert.size); | ||
| 1301 | PEM_read_bio_X509(membp, &rootCert, NULL, NULL); | ||
| 1302 | BIO_free(membp); | ||
| 1303 | if (SSL_CTX_use_certificate(ssl_ctx, rootCert) != 1) { | ||
| 1304 | debug_info("WARNING: Could not load RootCertificate"); | ||
| 1305 | } | ||
| 1306 | X509_free(rootCert); | ||
| 1307 | free(root_cert.data); | ||
| 1308 | |||
| 1309 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | ||
| 1310 | EVP_PKEY* rootPrivKey = NULL; | ||
| 1311 | membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); | ||
| 1312 | PEM_read_bio_PrivateKey(membp, &rootPrivKey, NULL, NULL); | ||
| 1313 | BIO_free(membp); | ||
| 1314 | if (SSL_CTX_use_PrivateKey(ssl_ctx, rootPrivKey) != 1) { | ||
| 1315 | debug_info("WARNING: Could not load RootPrivateKey"); | ||
| 1316 | } | ||
| 1317 | EVP_PKEY_free(rootPrivKey); | ||
| 1318 | #else | ||
| 1319 | RSA* rootPrivKey = NULL; | ||
| 1320 | membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); | ||
| 1321 | PEM_read_bio_RSAPrivateKey(membp, &rootPrivKey, NULL, NULL); | ||
| 1322 | BIO_free(membp); | ||
| 1323 | if (SSL_CTX_use_RSAPrivateKey(ssl_ctx, rootPrivKey) != 1) { | ||
| 1324 | debug_info("WARNING: Could not load RootPrivateKey"); | ||
| 1325 | } | ||
| 1326 | RSA_free(rootPrivKey); | ||
| 1327 | #endif | ||
| 1328 | free(root_privkey.data); | ||
| 1329 | |||
| 1330 | SSL *ssl = SSL_new(ssl_ctx); | ||
| 1331 | if (!ssl) { | ||
| 1332 | debug_info("ERROR: Could not create SSL object"); | ||
| 1333 | BIO_free(ssl_bio); | ||
| 1334 | SSL_CTX_free(ssl_ctx); | ||
| 1335 | return ret; | ||
| 1336 | } | ||
| 1337 | SSL_set_connect_state(ssl); | ||
| 1338 | SSL_set_verify(ssl, 0, ssl_verify_callback); | ||
| 1339 | SSL_set_bio(ssl, ssl_bio, ssl_bio); | ||
| 579 | 1340 | ||
| 1341 | debug_info("Performing SSL handshake"); | ||
| 1342 | int ssl_error = 0; | ||
| 1343 | do { | ||
| 1344 | ssl_error = SSL_get_error(ssl, SSL_do_handshake(ssl)); | ||
| 1345 | if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) { | ||
| 1346 | break; | ||
| 1347 | } | ||
| 1348 | #ifdef _WIN32 | ||
| 1349 | Sleep(100); | ||
| 1350 | #else | ||
| 1351 | struct timespec ts = { 0, 100000000 }; | ||
| 1352 | nanosleep(&ts, NULL); | ||
| 1353 | #endif | ||
| 1354 | } while (1); | ||
| 1355 | if (ssl_error != 0) { | ||
| 1356 | debug_info("ERROR during SSL handshake: %s", ssl_error_to_string(ssl_error)); | ||
| 1357 | SSL_free(ssl); | ||
| 1358 | SSL_CTX_free(ssl_ctx); | ||
| 1359 | } else { | ||
| 1360 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | ||
| 1361 | ssl_data_loc->session = ssl; | ||
| 1362 | ssl_data_loc->ctx = ssl_ctx; | ||
| 1363 | connection->ssl_data = ssl_data_loc; | ||
| 1364 | ret = IDEVICE_E_SUCCESS; | ||
| 1365 | debug_info("SSL mode enabled, %s, cipher: %s", SSL_get_version(ssl), SSL_get_cipher(ssl)); | ||
| 1366 | } | ||
| 1367 | /* required for proper multi-thread clean up to prevent leaks */ | ||
| 1368 | openssl_remove_thread_state(); | ||
| 1369 | #elif defined(HAVE_GNUTLS) | ||
| 580 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | 1370 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); |
| 581 | 1371 | ||
| 582 | /* Set up GnuTLS... */ | 1372 | /* Set up GnuTLS... */ |
| 583 | debug_info("enabling SSL mode"); | 1373 | debug_info("enabling SSL mode"); |
| 584 | errno = 0; | 1374 | errno = 0; |
| 585 | gnutls_global_init(); | ||
| 586 | gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); | 1375 | gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); |
| 587 | gnutls_certificate_client_set_retrieve_function (ssl_data_loc->certificate, internal_cert_callback); | 1376 | #if GNUTLS_VERSION_NUMBER >= 0x020b07 |
| 1377 | gnutls_certificate_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback); | ||
| 1378 | #else | ||
| 1379 | gnutls_certificate_client_set_retrieve_function(ssl_data_loc->certificate, internal_cert_callback); | ||
| 1380 | #endif | ||
| 588 | gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); | 1381 | gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); |
| 589 | { | 1382 | gnutls_priority_set_direct(ssl_data_loc->session, "NONE:+VERS-TLS1.0:+ANON-DH:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA1:+MD5:+COMP-NULL", NULL); |
| 590 | int protocol_priority[16] = { GNUTLS_SSL3, 0 }; | ||
| 591 | int kx_priority[16] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA, 0 }; | ||
| 592 | int cipher_priority[16] = { GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; | ||
| 593 | int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 }; | ||
| 594 | int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; | ||
| 595 | |||
| 596 | gnutls_cipher_set_priority(ssl_data_loc->session, cipher_priority); | ||
| 597 | gnutls_compression_set_priority(ssl_data_loc->session, comp_priority); | ||
| 598 | gnutls_kx_set_priority(ssl_data_loc->session, kx_priority); | ||
| 599 | gnutls_protocol_set_priority(ssl_data_loc->session, protocol_priority); | ||
| 600 | gnutls_mac_set_priority(ssl_data_loc->session, mac_priority); | ||
| 601 | } | ||
| 602 | gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); | 1383 | gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); |
| 603 | gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc); | 1384 | gnutls_session_set_ptr(ssl_data_loc->session, ssl_data_loc); |
| 604 | 1385 | ||
| @@ -607,10 +1388,13 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | |||
| 607 | gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); | 1388 | gnutls_x509_privkey_init(&ssl_data_loc->root_privkey); |
| 608 | gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); | 1389 | gnutls_x509_privkey_init(&ssl_data_loc->host_privkey); |
| 609 | 1390 | ||
| 610 | userpref_error_t uerr = userpref_get_keys_and_certs(ssl_data_loc->root_privkey, ssl_data_loc->root_cert, ssl_data_loc->host_privkey, ssl_data_loc->host_cert); | 1391 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, ssl_data_loc->root_cert); |
| 611 | if (uerr != USERPREF_E_SUCCESS) { | 1392 | pair_record_import_crt_with_name(pair_record, USERPREF_HOST_CERTIFICATE_KEY, ssl_data_loc->host_cert); |
| 612 | debug_info("Error %d when loading keys and certificates! %d", uerr); | 1393 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, ssl_data_loc->root_privkey); |
| 613 | } | 1394 | pair_record_import_key_with_name(pair_record, USERPREF_HOST_PRIVATE_KEY_KEY, ssl_data_loc->host_privkey); |
| 1395 | |||
| 1396 | if (pair_record) | ||
| 1397 | plist_free(pair_record); | ||
| 614 | 1398 | ||
| 615 | debug_info("GnuTLS step 1..."); | 1399 | debug_info("GnuTLS step 1..."); |
| 616 | gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); | 1400 | gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); |
| @@ -619,46 +1403,146 @@ idevice_error_t idevice_connection_enable_ssl(idevice_connection_t connection) | |||
| 619 | debug_info("GnuTLS step 3..."); | 1403 | debug_info("GnuTLS step 3..."); |
| 620 | gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); | 1404 | gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); |
| 621 | debug_info("GnuTLS step 4 -- now handshaking..."); | 1405 | debug_info("GnuTLS step 4 -- now handshaking..."); |
| 622 | if (errno) | 1406 | if (errno) { |
| 623 | debug_info("WARN: errno says %s before handshake!", strerror(errno)); | 1407 | debug_info("WARNING: errno says %s before handshake!", strerror(errno)); |
| 624 | return_me = gnutls_handshake(ssl_data_loc->session); | 1408 | } |
| 1409 | |||
| 1410 | int return_me = 0; | ||
| 1411 | do { | ||
| 1412 | return_me = gnutls_handshake(ssl_data_loc->session); | ||
| 1413 | } while(return_me == GNUTLS_E_AGAIN || return_me == GNUTLS_E_INTERRUPTED); | ||
| 1414 | |||
| 625 | debug_info("GnuTLS handshake done..."); | 1415 | debug_info("GnuTLS handshake done..."); |
| 626 | 1416 | ||
| 627 | if (return_me != GNUTLS_E_SUCCESS) { | 1417 | if (return_me != GNUTLS_E_SUCCESS) { |
| 628 | internal_ssl_cleanup(ssl_data_loc); | 1418 | internal_ssl_cleanup(ssl_data_loc); |
| 629 | free(ssl_data_loc); | 1419 | free(ssl_data_loc); |
| 630 | debug_info("GnuTLS reported something wrong."); | 1420 | debug_info("GnuTLS reported something wrong: %s", gnutls_strerror(return_me)); |
| 631 | gnutls_perror(return_me); | ||
| 632 | debug_info("oh.. errno says %s", strerror(errno)); | 1421 | debug_info("oh.. errno says %s", strerror(errno)); |
| 633 | } else { | 1422 | } else { |
| 634 | connection->ssl_data = ssl_data_loc; | 1423 | connection->ssl_data = ssl_data_loc; |
| 635 | ret = IDEVICE_E_SUCCESS; | 1424 | ret = IDEVICE_E_SUCCESS; |
| 636 | debug_info("SSL mode enabled"); | 1425 | debug_info("SSL mode enabled"); |
| 637 | } | 1426 | } |
| 1427 | #elif defined(HAVE_MBEDTLS) | ||
| 1428 | key_data_t root_cert = { NULL, 0 }; | ||
| 1429 | key_data_t root_privkey = { NULL, 0 }; | ||
| 1430 | |||
| 1431 | pair_record_import_crt_with_name(pair_record, USERPREF_ROOT_CERTIFICATE_KEY, &root_cert); | ||
| 1432 | pair_record_import_key_with_name(pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY, &root_privkey); | ||
| 1433 | |||
| 1434 | plist_free(pair_record); | ||
| 1435 | |||
| 1436 | ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_private)); | ||
| 1437 | |||
| 1438 | mbedtls_ssl_init(&ssl_data_loc->ctx); | ||
| 1439 | mbedtls_ssl_config_init(&ssl_data_loc->config); | ||
| 1440 | mbedtls_entropy_init(&ssl_data_loc->entropy); | ||
| 1441 | mbedtls_ctr_drbg_init(&ssl_data_loc->ctr_drbg); | ||
| 1442 | |||
| 1443 | int r = mbedtls_ctr_drbg_seed(&ssl_data_loc->ctr_drbg, mbedtls_entropy_func, &ssl_data_loc->entropy, NULL, 0); | ||
| 1444 | if (r != 0) { | ||
| 1445 | debug_info("ERROR: [mbedtls] mbedtls_ctr_drbg_seed failed: %d", r); | ||
| 1446 | return ret; | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | if (mbedtls_ssl_config_defaults(&ssl_data_loc->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { | ||
| 1450 | debug_info("ERROR: [mbedtls] Failed to set config defaults"); | ||
| 1451 | return ret; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | mbedtls_ssl_conf_rng(&ssl_data_loc->config, mbedtls_ctr_drbg_random, &ssl_data_loc->ctr_drbg); | ||
| 1455 | |||
| 1456 | mbedtls_ssl_conf_dbg(&ssl_data_loc->config, _mbedtls_log_cb, NULL); | ||
| 1457 | |||
| 1458 | mbedtls_ssl_conf_verify(&ssl_data_loc->config, cert_verify_cb, NULL); | ||
| 1459 | |||
| 1460 | mbedtls_ssl_setup(&ssl_data_loc->ctx, &ssl_data_loc->config); | ||
| 1461 | |||
| 1462 | mbedtls_ssl_set_bio(&ssl_data_loc->ctx, connection, (mbedtls_ssl_send_t*)&internal_ssl_write, (mbedtls_ssl_recv_t*)&internal_ssl_read, NULL); | ||
| 1463 | |||
| 1464 | mbedtls_x509_crt_init(&ssl_data_loc->certificate); | ||
| 1465 | |||
| 1466 | int crterr = mbedtls_x509_crt_parse(&ssl_data_loc->certificate, root_cert.data, root_cert.size); | ||
| 1467 | if (crterr < 0) { | ||
| 1468 | debug_info("ERROR: [mbedtls] parsing root cert failed: %d", crterr); | ||
| 1469 | return ret; | ||
| 1470 | } | ||
| 1471 | |||
| 1472 | mbedtls_ssl_conf_ca_chain(&ssl_data_loc->config, &ssl_data_loc->certificate, NULL); | ||
| 1473 | |||
| 1474 | mbedtls_pk_init(&ssl_data_loc->root_privkey); | ||
| 1475 | |||
| 1476 | #if MBEDTLS_VERSION_NUMBER >= 0x03000000 | ||
| 1477 | int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0, &_mbedtls_f_rng, NULL); | ||
| 1478 | #else | ||
| 1479 | int pkerr = mbedtls_pk_parse_key(&ssl_data_loc->root_privkey, root_privkey.data, root_privkey.size, NULL, 0); | ||
| 1480 | #endif | ||
| 1481 | if (pkerr < 0) { | ||
| 1482 | debug_info("ERROR: [mbedtls] parsing private key failed: %d (size=%d)", pkerr, root_privkey.size); | ||
| 1483 | return ret; | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | mbedtls_ssl_conf_own_cert(&ssl_data_loc->config, &ssl_data_loc->certificate, &ssl_data_loc->root_privkey); | ||
| 1487 | |||
| 1488 | int return_me = 0; | ||
| 1489 | do { | ||
| 1490 | return_me = mbedtls_ssl_handshake(&ssl_data_loc->ctx); | ||
| 1491 | } while (return_me == MBEDTLS_ERR_SSL_WANT_READ || return_me == MBEDTLS_ERR_SSL_WANT_WRITE); | ||
| 1492 | |||
| 1493 | if (return_me != 0) { | ||
| 1494 | debug_info("ERROR during SSL handshake: %d", return_me); | ||
| 1495 | internal_ssl_cleanup(ssl_data_loc); | ||
| 1496 | free(ssl_data_loc); | ||
| 1497 | } else { | ||
| 1498 | connection->ssl_data = ssl_data_loc; | ||
| 1499 | ret = IDEVICE_E_SUCCESS; | ||
| 1500 | debug_info("SSL mode enabled, %s, cipher: %s", mbedtls_ssl_get_version(&ssl_data_loc->ctx), mbedtls_ssl_get_ciphersuite(&ssl_data_loc->ctx)); | ||
| 1501 | debug_info("SSL mode enabled"); | ||
| 1502 | } | ||
| 1503 | #endif | ||
| 638 | return ret; | 1504 | return ret; |
| 639 | } | 1505 | } |
| 640 | 1506 | ||
| 641 | /** | ||
| 642 | * Disable SSL for the given connection. | ||
| 643 | * | ||
| 644 | * @param connection The connection to disable SSL for. | ||
| 645 | * | ||
| 646 | * @return IDEVICE_E_SUCCESS on success, IDEVICE_E_INVALID_ARG when connection | ||
| 647 | * is NULL. This function also returns IDEVICE_E_SUCCESS when SSL is not | ||
| 648 | * enabled and does no further error checking on cleanup. | ||
| 649 | */ | ||
| 650 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) | 1507 | idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) |
| 651 | { | 1508 | { |
| 1509 | return idevice_connection_disable_bypass_ssl(connection, 0); | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | idevice_error_t idevice_connection_disable_bypass_ssl(idevice_connection_t connection, uint8_t sslBypass) | ||
| 1513 | { | ||
| 652 | if (!connection) | 1514 | if (!connection) |
| 653 | return IDEVICE_E_INVALID_ARG; | 1515 | return IDEVICE_E_INVALID_ARG; |
| 654 | if (!connection->ssl_data) { | 1516 | if (!connection->ssl_data) { |
| 655 | /* ignore if ssl is not enabled */ | 1517 | /* ignore if ssl is not enabled */ |
| 656 | return IDEVICE_E_SUCCESS; | 1518 | return IDEVICE_E_SUCCESS; |
| 657 | } | 1519 | } |
| 658 | 1520 | ||
| 659 | if (connection->ssl_data->session) { | 1521 | // some services require plain text communication after SSL handshake |
| 660 | gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); | 1522 | // sending out SSL_shutdown will cause bytes |
| 1523 | if (!sslBypass) { | ||
| 1524 | #if defined(HAVE_OPENSSL) | ||
| 1525 | if (connection->ssl_data->session) { | ||
| 1526 | /* see: https://www.openssl.org/docs/ssl/SSL_shutdown.html#RETURN_VALUES */ | ||
| 1527 | if (SSL_shutdown(connection->ssl_data->session) == 0) { | ||
| 1528 | /* Only try bidirectional shutdown if we know it can complete */ | ||
| 1529 | int ssl_error; | ||
| 1530 | if ((ssl_error = SSL_get_error(connection->ssl_data->session, 0)) == SSL_ERROR_NONE) { | ||
| 1531 | SSL_shutdown(connection->ssl_data->session); | ||
| 1532 | } else { | ||
| 1533 | debug_info("Skipping bidirectional SSL shutdown. SSL error code: %i", ssl_error); | ||
| 1534 | } | ||
| 1535 | } | ||
| 1536 | } | ||
| 1537 | #elif defined(HAVE_GNUTLS) | ||
| 1538 | if (connection->ssl_data->session) { | ||
| 1539 | gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); | ||
| 1540 | } | ||
| 1541 | #elif defined(HAVE_MBEDTLS) | ||
| 1542 | mbedtls_ssl_close_notify(&connection->ssl_data->ctx); | ||
| 1543 | #endif | ||
| 661 | } | 1544 | } |
| 1545 | |||
| 662 | internal_ssl_cleanup(connection->ssl_data); | 1546 | internal_ssl_cleanup(connection->ssl_data); |
| 663 | free(connection->ssl_data); | 1547 | free(connection->ssl_data); |
| 664 | connection->ssl_data = NULL; | 1548 | connection->ssl_data = NULL; |
| @@ -668,3 +1552,27 @@ idevice_error_t idevice_connection_disable_ssl(idevice_connection_t connection) | |||
| 668 | return IDEVICE_E_SUCCESS; | 1552 | return IDEVICE_E_SUCCESS; |
| 669 | } | 1553 | } |
| 670 | 1554 | ||
| 1555 | const char* idevice_strerror(idevice_error_t err) | ||
| 1556 | { | ||
| 1557 | switch (err) { | ||
| 1558 | case IDEVICE_E_SUCCESS: | ||
| 1559 | return "Success"; | ||
| 1560 | case IDEVICE_E_INVALID_ARG: | ||
| 1561 | return "Invalid argument"; | ||
| 1562 | case IDEVICE_E_UNKNOWN_ERROR: | ||
| 1563 | return "Unknown Error"; | ||
| 1564 | case IDEVICE_E_NO_DEVICE: | ||
| 1565 | return "No device"; | ||
| 1566 | case IDEVICE_E_NOT_ENOUGH_DATA: | ||
| 1567 | return "Not enough data"; | ||
| 1568 | case IDEVICE_E_CONNREFUSED: | ||
| 1569 | return "Connection refused"; | ||
| 1570 | case IDEVICE_E_SSL_ERROR: | ||
| 1571 | return "SSL error"; | ||
| 1572 | case IDEVICE_E_TIMEOUT: | ||
| 1573 | return "Timeout"; | ||
| 1574 | default: | ||
| 1575 | break; | ||
| 1576 | } | ||
| 1577 | return "Unknown Error"; | ||
| 1578 | } | ||
