static gboolean generate_hashed_items (GkmSecretCollection *collection, EggBuffer *buffer) { GHashTable *attributes; const gchar *value; GList *items, *l; guint32 id, type; items = gkm_secret_collection_get_items (collection); egg_buffer_add_uint32 (buffer, g_list_length (items)); for (l = items; l; l = g_list_next (l)) { value = gkm_secret_object_get_identifier (l->data); if (!convert_to_integer (value, &id)) { g_warning ("trying to save a non-numeric item identifier '%s' into " "the keyring file format which only supports numeric.", value); continue; } egg_buffer_add_uint32 (buffer, id); value = gkm_secret_item_get_schema (l->data); type = gkm_secret_compat_parse_item_type (value); egg_buffer_add_uint32 (buffer, type); attributes = gkm_secret_item_get_fields (l->data); buffer_add_attributes (buffer, attributes, TRUE); } g_list_free (items); return !egg_buffer_has_error (buffer); }
int gkm_rpc_message_write_attribute_buffer (GkmRpcMessage *msg, CK_ATTRIBUTE_PTR arr, CK_ULONG num) { CK_ATTRIBUTE_PTR attr; CK_ULONG i; assert (!num || arr); assert (msg); /* Make sure this is in the rigth order */ assert (!msg->signature || gkm_rpc_message_verify_part (msg, "fA")); /* Write the number of items */ egg_buffer_add_uint32 (&msg->buffer, num); for (i = 0; i < num; ++i) { attr = &(arr[i]); /* The attribute type */ egg_buffer_add_uint32 (&msg->buffer, attr->type); /* And the attribute buffer length */ egg_buffer_add_uint32 (&msg->buffer, attr->pValue ? attr->ulValueLen : 0); } return !egg_buffer_has_error (&msg->buffer); }
static void buffer_add_attribute (EggBuffer *buffer, GHashTable *attributes, const gchar *key) { guint32 number; buffer_add_utf8_string (buffer, key); /* * COMPATIBILITY: * * Our new Secrets API doesn't support integer attributes. However, to have * compatibility with old keyring code reading this file, we need to set * the uint32 type attribute appropriately where expected. * * If there's an extra compat-uint32 attribute and the name of this attribute * is contained in that list, then write as a uint32. */ /* Determine if it's a uint32 compatible value, and store as such if it is */ if (gkm_secret_fields_get_compat_uint32 (attributes, key, &number)) { egg_buffer_add_uint32 (buffer, 1); egg_buffer_add_uint32 (buffer, number); /* A normal string attribute */ } else { egg_buffer_add_uint32 (buffer, 0); buffer_add_utf8_string (buffer, gkm_secret_fields_get (attributes, key)); } }
gboolean gkd_ssh_agent_proto_write_public_v1 (EggBuffer *resp, GckAttributes *attrs) { const GckAttribute *attr; gulong bits; g_assert (resp); g_assert (attrs); /* This is always an RSA key. */ /* Write out the number of bits of the key */ if (!gck_attributes_find_ulong (attrs, CKA_MODULUS_BITS, &bits)) g_return_val_if_reached (FALSE); egg_buffer_add_uint32 (resp, bits); /* Write out the exponent */ attr = gck_attributes_find (attrs, CKA_PUBLIC_EXPONENT); g_return_val_if_fail (attr, FALSE); if (!gkd_ssh_agent_proto_write_mpi_v1 (resp, attr)) return FALSE; /* Write out the modulus */ attr = gck_attributes_find (attrs, CKA_MODULUS); g_return_val_if_fail (attr, FALSE); if (!gkd_ssh_agent_proto_write_mpi_v1 (resp, attr)) return FALSE; return TRUE; }
static gpointer run_client_thread (gpointer data) { gint *socket = data; GkdSshAgentCall call; EggBuffer req; EggBuffer resp; guchar op; g_assert (GP11_IS_MODULE (pkcs11_module)); memset (&call, 0, sizeof (call)); call.sock = g_atomic_int_get (socket); g_assert (call.sock != -1); egg_buffer_init_full (&req, 128, egg_secure_realloc); egg_buffer_init_full (&resp, 128, (EggBufferAllocator)g_realloc); call.req = &req; call.resp = &resp; call.module = g_object_ref (pkcs11_module); for (;;) { egg_buffer_reset (call.req); /* 1. Read in the request */ if (!read_packet_with_size (&call)) break; /* 2. Now decode the operation */ if (!egg_buffer_get_byte (call.req, 4, NULL, &op)) break; if (op >= GKD_SSH_OP_MAX) break; g_assert (gkd_ssh_agent_operations[op]); /* 3. Execute the right operation */ egg_buffer_reset (call.resp); egg_buffer_add_uint32 (call.resp, 0); if (!(gkd_ssh_agent_operations[op]) (&call)) break; if (!egg_buffer_set_uint32 (call.resp, 0, call.resp->len - 4)) break; /* 4. Write the reply back out */ if (!write_all (call.sock, call.resp->buf, call.resp->len)) break; } egg_buffer_uninit (&req); egg_buffer_uninit (&resp); g_object_unref (call.module); close (call.sock); g_atomic_int_set (socket, -1); return NULL; }
int gkm_rpc_message_write_ulong_buffer (GkmRpcMessage *msg, CK_ULONG count) { assert (msg); /* Make sure this is in the right order */ assert (!msg->signature || gkm_rpc_message_verify_part (msg, "fu")); return egg_buffer_add_uint32 (&msg->buffer, count); }
static gboolean generate_encrypted_data (EggBuffer *buffer, GkmSecretCollection *collection, GkmSecretData *data) { GkmSecretObject *obj; GkmSecretItem *item; GList *items, *l; GHashTable *attributes; const gchar *label; GkmSecret *secret; GList *acl; int i; g_assert (buffer); g_assert (GKM_IS_SECRET_COLLECTION (collection)); g_assert (GKM_IS_SECRET_DATA (data)); /* Make sure we're using non-pageable memory */ egg_buffer_set_allocator (buffer, egg_secure_realloc); items = gkm_secret_collection_get_items (collection); for (l = items; l && !egg_buffer_has_error(buffer); l = g_list_next (l)) { item = GKM_SECRET_ITEM (l->data); obj = GKM_SECRET_OBJECT (l->data); label = gkm_secret_object_get_label (obj); buffer_add_utf8_string (buffer, label); secret = gkm_secret_data_get_secret (data, gkm_secret_object_get_identifier (obj)); buffer_add_secret (buffer, secret); if (!buffer_add_time (buffer, gkm_secret_object_get_created (obj)) || !buffer_add_time (buffer, gkm_secret_object_get_modified (obj))) break; /* reserved: */ if (!buffer_add_utf8_string (buffer, NULL)) break; for (i = 0; i < 4; i++) egg_buffer_add_uint32 (buffer, 0); attributes = gkm_secret_item_get_fields (item); if (!buffer_add_attributes (buffer, attributes, FALSE)) break; acl = g_object_get_data (G_OBJECT (item), "compat-acl"); if (!generate_acl_data (buffer, acl)) break; } g_list_free (items); /* Iteration completed prematurely == fail */ return (l == NULL); }
static gboolean buffer_add_attributes (EggBuffer *buffer, GHashTable *attributes, gboolean hashed) { GList *names, *l; g_assert (buffer); if (attributes == NULL) { egg_buffer_add_uint32 (buffer, 0); } else { names = gkm_secret_fields_get_names (attributes); egg_buffer_add_uint32 (buffer, g_list_length (names)); for (l = names; l; l = g_list_next (l)) { if (hashed) buffer_add_hashed_attribute (buffer, attributes, l->data); else buffer_add_attribute (buffer, attributes, l->data); } g_list_free (names); } return !egg_buffer_has_error (buffer); }
int gkm_rpc_message_write_attribute_array (GkmRpcMessage *msg, CK_ATTRIBUTE_PTR arr, CK_ULONG num) { CK_ULONG i; CK_ATTRIBUTE_PTR attr; unsigned char validity; assert (!num || arr); assert (msg); /* Make sure this is in the rigth order */ assert (!msg->signature || gkm_rpc_message_verify_part (msg, "aA")); /* Write the number of items */ egg_buffer_add_uint32 (&msg->buffer, num); for (i = 0; i < num; ++i) { attr = &(arr[i]); /* The attribute type */ egg_buffer_add_uint32 (&msg->buffer, attr->type); /* Write out the attribute validity */ validity = (((CK_LONG)attr->ulValueLen) == -1) ? 0 : 1; egg_buffer_add_byte (&msg->buffer, validity); /* The attribute length and value */ if (validity) { egg_buffer_add_uint32 (&msg->buffer, attr->ulValueLen); egg_buffer_add_byte_array (&msg->buffer, attr->pValue, attr->ulValueLen); } } return !egg_buffer_has_error (&msg->buffer); }
static gboolean generate_acl_data (EggBuffer *buffer, GList *acl) { GList *l; GkmSecretAccess *ac; egg_buffer_add_uint32 (buffer, g_list_length (acl)); for (l = acl; l != NULL; l = l->next) { ac = l->data; egg_buffer_add_uint32 (buffer, ac->types_allowed); if (!buffer_add_utf8_string (buffer, ac->display_name) || !buffer_add_utf8_string (buffer, ac->pathname)) return FALSE; /* Reserved: */ if (!buffer_add_utf8_string (buffer, NULL)) return FALSE; egg_buffer_add_uint32 (buffer, 0); } return TRUE; }
static void buffer_add_hashed_attribute (EggBuffer *buffer, GHashTable *attributes, const gchar *key) { guint32 number; gchar *value; buffer_add_utf8_string (buffer, key); /* See comments in buffer_add_attribute. */ /* Determine if it's a uint32 compatible value, and store as such if it is */ if (gkm_secret_fields_get_compat_hashed_uint32 (attributes, key, &number)) { egg_buffer_add_uint32 (buffer, 1); egg_buffer_add_uint32 (buffer, number); /* A standard string attribute */ } else { if (!gkm_secret_fields_get_compat_hashed_string (attributes, key, &value)) g_return_if_reached (); egg_buffer_add_uint32 (buffer, 0); buffer_add_utf8_string (buffer, value); g_free (value); } }
int gkm_rpc_message_write_byte_array (GkmRpcMessage *msg, CK_BYTE_PTR arr, CK_ULONG num) { assert (msg); /* Make sure this is in the right order */ assert (!msg->signature || gkm_rpc_message_verify_part (msg, "ay")); /* No array, no data, just length */ if (!arr) { egg_buffer_add_byte (&msg->buffer, 0); egg_buffer_add_uint32 (&msg->buffer, num); } else { egg_buffer_add_byte (&msg->buffer, 1); egg_buffer_add_byte_array (&msg->buffer, arr, num); } return !egg_buffer_has_error (&msg->buffer); }
int gkm_rpc_message_write_ulong_array (GkmRpcMessage *msg, CK_ULONG_PTR array, CK_ULONG n_array) { CK_ULONG i; assert (msg); /* Check that we're supposed to have this at this point */ assert (!msg->signature || gkm_rpc_message_verify_part (msg, "au")); /* We send a byte which determines whether there's actual data present or not */ egg_buffer_add_byte (&msg->buffer, array ? 1 : 0); egg_buffer_add_uint32 (&msg->buffer, n_array); /* Now send the data if valid */ if (array) { for (i = 0; i < n_array; ++i) egg_buffer_add_uint64 (&msg->buffer, array[i]); } return !egg_buffer_has_error (&msg->buffer); }
int gkm_rpc_message_prep (GkmRpcMessage *msg, int call_id, GkmRpcMessageType type) { int len; assert (type); assert (call_id >= GKM_RPC_CALL_ERROR); assert (call_id < GKM_RPC_CALL_MAX); gkm_rpc_message_reset (msg); if (call_id != GKM_RPC_CALL_ERROR) { /* The call id and signature */ if (type == GKM_RPC_REQUEST) msg->signature = gkm_rpc_calls[call_id].request; else if (type == GKM_RPC_RESPONSE) msg->signature = gkm_rpc_calls[call_id].response; else assert (0 && "invalid message type"); assert (msg->signature); msg->sigverify = msg->signature; } msg->call_id = call_id; msg->call_type = type; /* Encode the two of them */ egg_buffer_add_uint32 (&msg->buffer, call_id); if (msg->signature) { len = strlen (msg->signature); egg_buffer_add_byte_array (&msg->buffer, (unsigned char*)msg->signature, len); } msg->parsed = 0; return !egg_buffer_has_error (&msg->buffer); }
GkmDataResult gkm_secret_binary_write (GkmSecretCollection *collection, GkmSecretData *sdata, gpointer *data, gsize *n_data) { GkmSecretObject *obj; EggBuffer to_encrypt; GkmSecret *master; guchar digest[16]; EggBuffer buffer; gint hash_iterations; gint lock_timeout; guchar salt[8]; guint flags = 0; int i; g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (collection), GKM_DATA_FAILURE); g_return_val_if_fail (GKM_IS_SECRET_DATA (sdata), GKM_DATA_LOCKED); g_return_val_if_fail (data && n_data, GKM_DATA_FAILURE); g_return_val_if_fail (gcry_md_get_algo_dlen (GCRY_MD_MD5) == sizeof (digest), GKM_DATA_FAILURE); obj = GKM_SECRET_OBJECT (collection); egg_buffer_init_full (&buffer, 256, g_realloc); /* Prepare the keyring for encryption */ hash_iterations = g_random_int_range (1000, 4096); gcry_create_nonce (salt, sizeof (salt)); egg_buffer_append (&buffer, (guchar*)KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN); egg_buffer_add_byte (&buffer, 0); /* Major version */ egg_buffer_add_byte (&buffer, 0); /* Minor version */ egg_buffer_add_byte (&buffer, 0); /* crypto (0 == AES) */ egg_buffer_add_byte (&buffer, 0); /* hash (0 == MD5) */ buffer_add_utf8_string (&buffer, gkm_secret_object_get_label (obj)); buffer_add_time (&buffer, gkm_secret_object_get_modified (obj)); buffer_add_time (&buffer, gkm_secret_object_get_created (obj)); lock_timeout = gkm_secret_collection_get_lock_idle (collection); if (lock_timeout) { flags |= LOCK_ON_IDLE_FLAG; } else { lock_timeout = gkm_secret_collection_get_lock_after (collection); if (lock_timeout) flags |= LOCK_AFTER_FLAG; } egg_buffer_add_uint32 (&buffer, flags); egg_buffer_add_uint32 (&buffer, lock_timeout); egg_buffer_add_uint32 (&buffer, hash_iterations); egg_buffer_append (&buffer, salt, 8); /* Reserved: */ for (i = 0; i < 4; i++) egg_buffer_add_uint32 (&buffer, 0); /* Hashed items: */ generate_hashed_items (collection, &buffer); /* Encrypted data. Use non-pageable memory */ egg_buffer_init_full (&to_encrypt, 4096, egg_secure_realloc); egg_buffer_append (&to_encrypt, (guchar*)digest, 16); /* Space for hash */ if (!generate_encrypted_data (&to_encrypt, collection, sdata)) { egg_buffer_uninit (&to_encrypt); egg_buffer_uninit (&buffer); return GKM_DATA_FAILURE; } /* Pad with zeros to multiple of 16 bytes */ while (to_encrypt.len % 16 != 0) egg_buffer_add_byte (&to_encrypt, 0); gcry_md_hash_buffer (GCRY_MD_MD5, (void*)digest, (guchar*)to_encrypt.buf + 16, to_encrypt.len - 16); memcpy (to_encrypt.buf, digest, 16); /* If no master password is set, we shouldn't be writing binary... */ master = gkm_secret_data_get_master (sdata); g_return_val_if_fail (master, GKM_DATA_FAILURE); if (!encrypt_buffer (&to_encrypt, master, salt, hash_iterations)) { egg_buffer_uninit (&buffer); egg_buffer_uninit (&to_encrypt); return GKM_DATA_FAILURE; } if (egg_buffer_has_error (&to_encrypt) || egg_buffer_has_error (&buffer)) { egg_buffer_uninit (&buffer); egg_buffer_uninit (&to_encrypt); return GKM_DATA_FAILURE; } egg_buffer_add_uint32 (&buffer, to_encrypt.len); egg_buffer_append (&buffer, to_encrypt.buf, to_encrypt.len); egg_buffer_uninit (&to_encrypt); *data = egg_buffer_uninit_steal (&buffer, n_data); return GKM_DATA_SUCCESS; }