static void *thread_ssh_buffer_get_format_error(void *threadid) { ssh_buffer buffer = NULL; uint8_t b = 0; uint16_t w = 0; uint32_t d = 0; uint64_t q = 0; ssh_string s = NULL; char *s1 = NULL, *s2 = NULL; int rc; uint8_t verif[] = "\x42\x13\x37\x0b\xad\xc0\xde\x13\x24\x35\x46" "\xac\xbd\xce\xdf" "\x00\x00\x00\x06" "libssh" "\x00\x00\x00\x05" "rocks" "So much"; /* Unused */ (void) threadid; /* Setup */ buffer = ssh_buffer_new(); if (buffer == NULL) { pthread_exit((void *)-1); } ssh_buffer_set_secure(buffer); rc = ssh_buffer_add_data(buffer, verif, sizeof(verif) - 1); assert_int_equal(rc, SSH_OK); rc = ssh_buffer_unpack(buffer, "bwdqSsPb", &b, &w, &d, &q, &s, &s1, (size_t)7, &s2, &b); assert_int_equal(rc, SSH_ERROR); assert_null(s); assert_null(s1); assert_null(s2); /* Teardown */ SSH_BUFFER_FREE(buffer); pthread_exit(NULL); }
static void *thread_buffer_pack_badformat(void *threadid) { ssh_buffer buffer = NULL; uint8_t b = 42; int rc; /* Unused */ (void) threadid; /* Setup */ buffer = ssh_buffer_new(); if (buffer == NULL) { pthread_exit((void *)-1); } ssh_buffer_set_secure(buffer); /* first with missing format */ rc = ssh_buffer_pack(buffer, "b", b, b); assert_int_equal(rc, SSH_ERROR); ssh_buffer_reinit(buffer); /* with additional format */ rc = ssh_buffer_pack(buffer, "bb", b); /* check that we detect the missing parameter */ assert_int_equal(rc, SSH_ERROR); /* unpack with missing format */ ssh_buffer_reinit(buffer); rc = ssh_buffer_pack(buffer, "bb", 42, 43); assert_int_equal(rc, SSH_OK); rc = ssh_buffer_unpack(buffer, "b", &b, &b); assert_int_equal(rc, SSH_ERROR); /* not doing the test with additional format as * it could crash the process */ /* Teardown */ SSH_BUFFER_FREE(buffer); pthread_exit(NULL); }
sftp_client_message sftp_get_client_message(sftp_session sftp) { ssh_session session = sftp->session; sftp_packet packet; sftp_client_message msg; ssh_buffer payload; int rc; msg = malloc(sizeof (struct sftp_client_message_struct)); if (msg == NULL) { ssh_set_error_oom(session); return NULL; } ZERO_STRUCTP(msg); packet = sftp_packet_read(sftp); if (packet == NULL) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } payload = packet->payload; msg->type = packet->type; msg->sftp = sftp; /* take a copy of the whole packet */ msg->complete_message = ssh_buffer_new(); ssh_buffer_add_data(msg->complete_message, ssh_buffer_get(payload), ssh_buffer_get_len(payload)); ssh_buffer_get_u32(payload, &msg->id); switch(msg->type) { case SSH_FXP_CLOSE: case SSH_FXP_READDIR: msg->handle = ssh_buffer_get_ssh_string(payload); if (msg->handle == NULL) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } break; case SSH_FXP_READ: rc = ssh_buffer_unpack(payload, "Sqd", &msg->handle, &msg->offset, &msg->len); if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } break; case SSH_FXP_WRITE: rc = ssh_buffer_unpack(payload, "SqS", &msg->handle, &msg->offset, &msg->data); if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } break; case SSH_FXP_REMOVE: case SSH_FXP_RMDIR: case SSH_FXP_OPENDIR: case SSH_FXP_READLINK: case SSH_FXP_REALPATH: rc = ssh_buffer_unpack(payload, "s", &msg->filename); if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } break; case SSH_FXP_RENAME: case SSH_FXP_SYMLINK: rc = ssh_buffer_unpack(payload, "sS", &msg->filename, &msg->data); if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } break; case SSH_FXP_MKDIR: case SSH_FXP_SETSTAT: rc = ssh_buffer_unpack(payload, "s", &msg->filename); if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } msg->attr = sftp_parse_attr(sftp, payload, 0); if (msg->attr == NULL) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } break; case SSH_FXP_FSETSTAT: msg->handle = ssh_buffer_get_ssh_string(payload); if (msg->handle == NULL) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } msg->attr = sftp_parse_attr(sftp, payload, 0); if (msg->attr == NULL) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } break; case SSH_FXP_LSTAT: case SSH_FXP_STAT: rc = ssh_buffer_unpack(payload, "s", &msg->filename); if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } if(sftp->version > 3) { ssh_buffer_unpack(payload, "d", &msg->flags); } break; case SSH_FXP_OPEN: rc = ssh_buffer_unpack(payload, "sd", &msg->filename, &msg->flags); if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } msg->attr = sftp_parse_attr(sftp, payload, 0); if (msg->attr == NULL) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } break; case SSH_FXP_FSTAT: rc = ssh_buffer_unpack(payload, "S", &msg->handle); if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } break; default: ssh_set_error(sftp->session, SSH_FATAL, "Received unhandled sftp message %d", msg->type); sftp_client_message_free(msg); return NULL; } sftp_packet_free(packet); return msg; }
/** * @internal * * @brief Import a private key from a ssh buffer. * * @param[in] key_blob_buffer The key blob to import as specified in * key.c:key_private_serialize in OpenSSH source * code. * * @param[out] pkey A pointer where the allocated key can be stored. You * need to free the memory. * * @return SSH_OK on success, SSH_ERROR on error. * * @see ssh_key_free() */ static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer, ssh_key *pkey) { enum ssh_keytypes_e type; char *type_s = NULL; ssh_key key = NULL; ssh_string pubkey = NULL, privkey = NULL; int rc; if (pkey == NULL) { return SSH_ERROR; } rc = ssh_buffer_unpack(key_blob_buffer, "s", &type_s); if (rc == SSH_ERROR){ ssh_pki_log("Unpack error"); return SSH_ERROR; } type = ssh_key_type_from_name(type_s); if (type == SSH_KEYTYPE_UNKNOWN) { ssh_pki_log("Unknown key type found!"); return SSH_ERROR; } SAFE_FREE(type_s); key = ssh_key_new(); if (key == NULL) { ssh_pki_log("Out of memory"); return SSH_ERROR; } key->type = type; key->type_c = ssh_key_type_to_char(type); key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; switch (type) { case SSH_KEYTYPE_ED25519: rc = ssh_buffer_unpack(key_blob_buffer, "SS", &pubkey, &privkey); if (rc != SSH_OK){ ssh_pki_log("Unpack error"); goto fail; } if(ssh_string_len(pubkey) != ED25519_PK_LEN || ssh_string_len(privkey) != ED25519_SK_LEN){ ssh_pki_log("Invalid ed25519 key len"); goto fail; } key->ed25519_privkey = malloc(ED25519_SK_LEN); key->ed25519_pubkey = malloc(ED25519_PK_LEN); if(key->ed25519_privkey == NULL || key->ed25519_pubkey == NULL){ goto fail; } memcpy(key->ed25519_privkey, ssh_string_data(privkey), ED25519_SK_LEN); memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_PK_LEN); memset(ssh_string_data(privkey), 0, ED25519_SK_LEN); SAFE_FREE(privkey); SAFE_FREE(pubkey); break; case SSH_KEYTYPE_DSS: /* p,q,g,pub_key,priv_key */ case SSH_KEYTYPE_RSA: /* n,e,d,iqmp,p,q */ case SSH_KEYTYPE_RSA1: case SSH_KEYTYPE_ECDSA: /* curve_name, group, privkey */ ssh_pki_log("Unsupported private key method %s", key->type_c); goto fail; case SSH_KEYTYPE_UNKNOWN: ssh_pki_log("Unknown private key protocol %s", key->type_c); goto fail; } *pkey = key; return SSH_OK; fail: ssh_key_free(key); if(privkey != NULL){ memset(ssh_string_data(privkey), 0, ssh_string_len(privkey)); } SAFE_FREE(pubkey); SAFE_FREE(privkey); return SSH_ERROR; }
/** @internal * @brief Import a private key in OpenSSH (new) format. This format is * typically used with ed25519 keys but can be used for others. */ ssh_key ssh_pki_openssh_privkey_import(const char *text_key, const char *passphrase, ssh_auth_callback auth_fn, void *auth_data) { const char *ptr=text_key; const char *end; char *base64; int cmp; int rc; int i; ssh_buffer buffer = NULL, privkey_buffer=NULL; char *magic = NULL, *ciphername = NULL, *kdfname = NULL; uint32_t nkeys = 0, checkint1, checkint2; ssh_string kdfoptions = NULL; ssh_string pubkey0 = NULL; ssh_string privkeys = NULL; ssh_string comment = NULL; ssh_key key = NULL; uint8_t padding; cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN)); if (cmp != 0){ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no header)"); goto error; } ptr += strlen(OPENSSH_HEADER_BEGIN); while(ptr[0] != '\0' && !isspace((int)ptr[0])) { ptr++; } end = strstr(ptr, OPENSSH_HEADER_END); if (end == NULL){ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no footer)"); goto error; } base64 = malloc(end - ptr + 1); if (base64 == NULL){ goto error; } for (i = 0; ptr < end; ptr++){ if (!isspace((int)ptr[0])) { base64[i] = ptr[0]; i++; } } base64[i] = '\0'; buffer = base64_to_bin(base64); SAFE_FREE(base64); if (buffer == NULL){ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (base64 error)"); goto error; } rc = ssh_buffer_unpack(buffer, "PssSdSS", strlen(OPENSSH_AUTH_MAGIC) + 1, &magic, &ciphername, &kdfname, &kdfoptions, &nkeys, &pubkey0, &privkeys); if (rc == SSH_ERROR){ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (unpack error)"); goto error; } cmp = strncmp(magic, OPENSSH_AUTH_MAGIC, strlen(OPENSSH_AUTH_MAGIC)); if (cmp != 0){ SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (bad magic)"); goto error; } ssh_pki_log("Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d\n", ciphername, kdfname, nkeys); if (nkeys != 1){ SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys); goto error; } rc = pki_private_key_decrypt(privkeys, passphrase, ciphername, kdfname, kdfoptions, auth_fn, auth_data); if (rc == SSH_ERROR){ goto error; } privkey_buffer = ssh_buffer_new(); if (privkey_buffer == NULL) { rc = SSH_ERROR; goto error; } ssh_buffer_set_secure(privkey_buffer); ssh_buffer_add_data(privkey_buffer, ssh_string_data(privkeys), ssh_string_len(privkeys)); rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2); if (rc == SSH_ERROR || checkint1 != checkint2){ SSH_LOG(SSH_LOG_WARN, "OpenSSH private key unpack error (correct password?)"); goto error; } rc = pki_openssh_import_privkey_blob(privkey_buffer, &key); if (rc == SSH_ERROR){ goto error; } comment = buffer_get_ssh_string(privkey_buffer); SAFE_FREE(comment); /* verify that the remaining data is correct padding */ for (i=1; buffer_get_rest_len(privkey_buffer) > 0; ++i){ buffer_get_u8(privkey_buffer, &padding); if (padding != i){ ssh_key_free(key); key = NULL; ssh_pki_log("Invalid padding"); goto error; } } error: if(buffer != NULL){ ssh_buffer_free(buffer); buffer = NULL; } if(privkey_buffer != NULL){ ssh_buffer_free(privkey_buffer); privkey_buffer = NULL; } SAFE_FREE(magic); SAFE_FREE(ciphername); SAFE_FREE(kdfname); SAFE_FREE(kdfoptions); SAFE_FREE(pubkey0); SAFE_FREE(privkeys); return key; }
/** * @brief decrypts an encrypted ed25519 private key blob * */ static int pki_private_key_decrypt(ssh_string blob, const char* passphrase, const char *ciphername, const char *kdfname, ssh_string kdfoptions, ssh_auth_callback auth_fn, void *auth_data) { struct ssh_cipher_struct *ciphers = ssh_get_ciphertab(); struct ssh_cipher_struct cipher; uint8_t key_material[128]; char passphrase_buffer[128]; size_t key_material_len; ssh_buffer buffer; ssh_string salt; uint32_t rounds; int cmp; int rc; int i; cmp = strcmp(ciphername, "none"); if (cmp == 0){ /* no decryption required */ return SSH_OK; } for (i = 0; ciphers[i].name != NULL; i++) { cmp = strcmp(ciphername, ciphers[i].name); if (cmp == 0){ memcpy(&cipher, &ciphers[i], sizeof(cipher)); break; } } if (ciphers[i].name == NULL){ SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername); return SSH_ERROR; } cmp = strcmp(kdfname, "bcrypt"); if (cmp != 0) { SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname); return SSH_ERROR; } if (ssh_string_len(blob) % cipher.blocksize != 0) { SSH_LOG(SSH_LOG_WARN, "Encrypted string not multiple of blocksize: %zu", ssh_string_len(blob)); return SSH_ERROR; } buffer = ssh_buffer_new(); if (buffer == NULL){ return SSH_ERROR; } rc = ssh_buffer_add_data(buffer, ssh_string_data(kdfoptions), ssh_string_len(kdfoptions)); if (rc != SSH_ERROR){ rc = ssh_buffer_unpack(buffer, "Sd", &salt, &rounds); } ssh_buffer_free(buffer); if (rc == SSH_ERROR){ return SSH_ERROR; } /* We need material for key (keysize bits / 8) and IV (blocksize) */ key_material_len = cipher.keysize/8 + cipher.blocksize; if (key_material_len > sizeof(key_material)) { ssh_pki_log("Key material too big"); return SSH_ERROR; } ssh_pki_log("Decryption: %d key, %d IV, %d rounds, %zu bytes salt", cipher.keysize/8, cipher.blocksize, rounds, ssh_string_len(salt)); if (passphrase == NULL) { if (auth_fn == NULL) { SAFE_FREE(salt); ssh_pki_log("No passphrase provided"); return SSH_ERROR; } rc = auth_fn("Passphrase", passphrase_buffer, sizeof(passphrase_buffer), 0, 0, auth_data); if (rc != SSH_OK) { SAFE_FREE(salt); return SSH_ERROR; } passphrase = passphrase_buffer; } rc = bcrypt_pbkdf(passphrase, strlen(passphrase), ssh_string_data(salt), ssh_string_len(salt), key_material, key_material_len, rounds); SAFE_FREE(salt); if (rc < 0){ return SSH_ERROR; } BURN_BUFFER(passphrase_buffer, sizeof(passphrase_buffer)); cipher.set_decrypt_key(&cipher, key_material, key_material + cipher.keysize/8); cipher.decrypt(&cipher, ssh_string_data(blob), ssh_string_data(blob), ssh_string_len(blob)); ssh_cipher_clear(&cipher); return SSH_OK; }
static void *thread_ssh_buffer_get_format(void *threadid) { ssh_buffer buffer; uint8_t b = 0; uint16_t w = 0; uint32_t d = 0; uint64_t q = 0; ssh_string s = NULL; char *s1 = NULL, *s2 = NULL; int rc; size_t len; uint8_t verif[] = "\x42\x13\x37\x0b\xad\xc0\xde\x13\x24\x35\x46" "\xac\xbd\xce\xdf" "\x00\x00\x00\x06" "libssh" "\x00\x00\x00\x05" "rocks" "So much"; /* Unused */ (void) threadid; /* Setup */ buffer = ssh_buffer_new(); if (buffer == NULL) { pthread_exit((void *)-1); } ssh_buffer_set_secure(buffer); rc = ssh_buffer_add_data(buffer, verif, sizeof(verif) - 1); assert_int_equal(rc, SSH_OK); rc = ssh_buffer_unpack(buffer, "bwdqSsP", &b, &w, &d, &q, &s, &s1, (size_t)7, &s2); assert_int_equal(rc, SSH_OK); assert_int_equal(b, 0x42); assert_int_equal(w, 0x1337); assert_true(d == 0xbadc0de); assert_true(q == 0x13243546acbdcedf); assert_non_null(s); assert_int_equal(ssh_string_len(s), 6); assert_memory_equal(ssh_string_data(s), "libssh", 6); assert_non_null(s1); assert_string_equal(s1, "rocks"); assert_non_null(s2); assert_memory_equal(s2, "So much", 7); len = ssh_buffer_get_len(buffer); assert_int_equal(len, 0); SAFE_FREE(s); SAFE_FREE(s1); SAFE_FREE(s2); /* Teardown */ SSH_BUFFER_FREE(buffer); pthread_exit(NULL); }