/** * cdk_stream_mmap: * @s: the stream * @ret_buf: the buffer to store the content * @ret_count: length of the buffer * * Map the data of the given stream into a memory section. @ret_count * contains the length of the buffer. **/ cdk_error_t cdk_stream_mmap( cdk_stream_t s, byte ** ret_buf, size_t * ret_count ) { const u32 max_filesize = 16777216; u32 len, oldpos; int n, rc; char * p; if( !s || !ret_buf || !ret_count ) return CDK_Inv_Value; *ret_count = 0; *ret_buf = NULL; oldpos = cdk_stream_tell( s ); rc = cdk_stream_flush( s ); if( !rc ) rc = cdk_stream_seek( s, 0 ); if( rc ) return rc; len = cdk_stream_get_length( s ); if( !len || len > max_filesize ) return 0; p = *ret_buf = cdk_calloc( 1, len+1 ); if( !p ) return 0; *ret_count = len; n = cdk_stream_read( s, p, len ); if( n != len ) *ret_count = n; rc = cdk_stream_seek( s, oldpos ); return rc; }
/* This functions builds an index of the keyring into a separate file with the name keyring.ext.idx. It contains the offset of all public- and public subkeys. The format of the file is: -------- 4 octets offset of the packet 8 octets keyid 20 octets fingerprint -------- We store the keyid and the fingerprint due to the fact we can't get the keyid from a v3 fingerprint directly. */ static int keydb_idx_build( const char * file ) { cdk_packet_t pkt; cdk_stream_t inp, out = NULL; byte buf[8], fpr[20]; char * fname; u32 keyid[2]; int rc, pos; if( !file ) return CDK_Inv_Value; pkt = cdk_calloc( 1, sizeof * pkt ); if( !pkt ) return CDK_Out_Of_Core; fname = keydb_idx_mkname( file ); if( !fname ) { rc = CDK_Out_Of_Core; goto leave; } rc = cdk_stream_open( file, &inp ); if( !rc ) rc = cdk_stream_create( fname, &out ); if( rc ) goto leave; while( !cdk_stream_eof( inp ) ) { pos = cdk_stream_tell( inp ); rc = cdk_pkt_read( inp, pkt ); if( rc ) break; if( pkt->pkttype == CDK_PKT_PUBLIC_KEY || pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY ) { _cdk_u32tobuf( pos, buf ); cdk_stream_write( out, buf, 4 ); cdk_pk_get_keyid( pkt->pkt.public_key, keyid ); _cdk_u32tobuf( keyid[0], buf ); _cdk_u32tobuf( keyid[1], buf + 4 ); cdk_stream_write( out, buf, 8 ); cdk_pk_get_fingerprint( pkt->pkt.public_key, fpr ); cdk_stream_write( out, fpr, 20 ); } cdk_pkt_free( pkt ); cdk_pkt_init( pkt ); } cdk_stream_close( out ); leave: cdk_stream_close( inp ); cdk_free( fname ); cdk_free( pkt ); return rc; }
/** * cdk_stream_peek: * @inp: the input stream handle * @s: buffer * @count: number of bytes to peek * * The function acts like cdk_stream_read with the difference that * the file pointer is moved to the old position after the bytes were read. **/ int cdk_stream_peek( cdk_stream_t inp, byte *s, size_t count ) { unsigned off; int nbytes, rc; if( !inp || !s ) return CDK_Inv_Value; off = cdk_stream_tell( inp ); nbytes = _cdk_stream_gets( inp, s, count ); rc = cdk_stream_seek( inp, off ); if( rc ) return 0; return nbytes; }
/** * cdk_stream_peek: * @inp: the input stream handle * @s: buffer * @count: number of bytes to peek * * The function acts like cdk_stream_read with the difference that * the file pointer is moved to the old position after the bytes were read. **/ int cdk_stream_peek (cdk_stream_t inp, byte * buf, size_t buflen) { off_t off; int nbytes; if (!inp || !buf) return 0; if (inp->cbs_hd) return 0; off = cdk_stream_tell (inp); nbytes = cdk_stream_read (inp, buf, buflen); if (nbytes == -1) return 0; if (cdk_stream_seek (inp, off)) return 0; return nbytes; }
/** * cdk_stream_mmap_part: * @s: the stream * @off: the offset where to start * @len: how much bytes shall be mapped * @ret_buf: the buffer to store the content * @ret_buflen: length of the buffer * * Maps the data of the given stream into a memory section. @ret_count * contains the length of the buffer. **/ cdk_error_t cdk_stream_mmap_part (cdk_stream_t s, off_t off, size_t len, byte ** ret_buf, size_t * ret_buflen) { cdk_error_t rc; off_t oldpos; unsigned int n; if (!ret_buf || !ret_buflen) { gnutls_assert (); return CDK_Inv_Value; } *ret_buf = NULL; *ret_buflen = 0; if (!s) { gnutls_assert (); return CDK_Inv_Value; } /* Memory mapping is not supported on custom I/O objects. */ if (s->cbs_hd) { _cdk_log_debug ("cdk_stream_mmap_part: not supported on callbacks\n"); gnutls_assert (); return CDK_Inv_Mode; } oldpos = cdk_stream_tell (s); rc = cdk_stream_flush (s); if (rc) { gnutls_assert (); return rc; } rc = cdk_stream_seek (s, off); if (rc) { gnutls_assert (); return rc; } if (!len) len = cdk_stream_get_length (s); if (!len) { _cdk_log_debug ("cdk_stream_mmap_part: invalid file size %lu\n", len); gnutls_assert (); return s->error; } if (len > MAX_MAP_SIZE) { gnutls_assert (); return CDK_Too_Short; } *ret_buf = cdk_calloc (1, len + 1); *ret_buflen = len; n = cdk_stream_read (s, *ret_buf, len); if (n != len) *ret_buflen = n; rc = cdk_stream_seek (s, oldpos); if (rc) gnutls_assert (); return rc; }
/** * cdk_pkt_read: * @inp: the input stream * @pkt: allocated packet handle to store the packet * * Parse the next packet on the @inp stream and return its contents in @pkt. **/ cdk_error_t cdk_pkt_read (cdk_stream_t inp, cdk_packet_t pkt) { int ctb, is_newctb; int pkttype; size_t pktlen = 0, pktsize = 0, is_partial = 0; cdk_error_t rc; if (!inp || !pkt) return CDK_Inv_Value; ctb = cdk_stream_getc (inp); if (cdk_stream_eof (inp) || ctb == EOF) return CDK_EOF; else if (!ctb) return CDK_Inv_Packet; pktsize++; if (!(ctb & 0x80)) { _cdk_log_info ("cdk_pkt_read: no openpgp data found. " "(ctb=%02X; fpos=%02X)\n", ctb, cdk_stream_tell (inp)); return CDK_Inv_Packet; } if (ctb & 0x40) /* RFC2440 packet format. */ { pkttype = ctb & 0x3f; is_newctb = 1; } else /* the old RFC1991 packet format. */ { pkttype = ctb & 0x3f; pkttype >>= 2; is_newctb = 0; } if (pkttype > 63) { _cdk_log_info ("cdk_pkt_read: unknown type %d\n", pkttype); return CDK_Inv_Packet; } if (is_newctb) read_new_length (inp, &pktlen, &pktsize, &is_partial); else read_old_length (inp, ctb, &pktlen, &pktsize); pkt->pkttype = pkttype; pkt->pktlen = pktlen; pkt->pktsize = pktsize + pktlen; pkt->old_ctb = is_newctb ? 0 : 1; rc = 0; switch (pkt->pkttype) { case CDK_PKT_ATTRIBUTE: pkt->pkt.user_id = cdk_calloc (1, sizeof *pkt->pkt.user_id + pkt->pktlen + 16 + 1); if (!pkt->pkt.user_id) return CDK_Out_Of_Core; pkt->pkt.user_id->name = (char*)pkt->pkt.user_id + sizeof(*pkt->pkt.user_id); rc = read_attribute (inp, pktlen, pkt->pkt.user_id); pkt->pkttype = CDK_PKT_ATTRIBUTE; break; case CDK_PKT_USER_ID: pkt->pkt.user_id = cdk_calloc (1, sizeof *pkt->pkt.user_id + pkt->pktlen + 1); if (!pkt->pkt.user_id) return CDK_Out_Of_Core; pkt->pkt.user_id->name = (char*)pkt->pkt.user_id + sizeof(*pkt->pkt.user_id); rc = read_user_id (inp, pktlen, pkt->pkt.user_id); break; case CDK_PKT_PUBLIC_KEY: pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key); if (!pkt->pkt.public_key) return CDK_Out_Of_Core; rc = read_public_key (inp, pktlen, pkt->pkt.public_key); break; case CDK_PKT_PUBLIC_SUBKEY: pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key); if (!pkt->pkt.public_key) return CDK_Out_Of_Core; rc = read_public_subkey (inp, pktlen, pkt->pkt.public_key); break; case CDK_PKT_SECRET_KEY: pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key); if (!pkt->pkt.secret_key) return CDK_Out_Of_Core; pkt->pkt.secret_key->pk = cdk_calloc (1, sizeof *pkt->pkt.secret_key->pk); if (!pkt->pkt.secret_key->pk) return CDK_Out_Of_Core; rc = read_secret_key (inp, pktlen, pkt->pkt.secret_key); break; case CDK_PKT_SECRET_SUBKEY: pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key); if (!pkt->pkt.secret_key) return CDK_Out_Of_Core; pkt->pkt.secret_key->pk = cdk_calloc (1, sizeof *pkt->pkt.secret_key->pk); if (!pkt->pkt.secret_key->pk) return CDK_Out_Of_Core; rc = read_secret_subkey (inp, pktlen, pkt->pkt.secret_key); break; case CDK_PKT_LITERAL: pkt->pkt.literal = cdk_calloc (1, sizeof *pkt->pkt.literal); if (!pkt->pkt.literal) return CDK_Out_Of_Core; rc = read_literal (inp, pktlen, &pkt->pkt.literal, is_partial); break; case CDK_PKT_ONEPASS_SIG: pkt->pkt.onepass_sig = cdk_calloc (1, sizeof *pkt->pkt.onepass_sig); if (!pkt->pkt.onepass_sig) return CDK_Out_Of_Core; rc = read_onepass_sig (inp, pktlen, pkt->pkt.onepass_sig); break; case CDK_PKT_SIGNATURE: pkt->pkt.signature = cdk_calloc (1, sizeof *pkt->pkt.signature); if (!pkt->pkt.signature) return CDK_Out_Of_Core; rc = read_signature (inp, pktlen, pkt->pkt.signature); break; case CDK_PKT_PUBKEY_ENC: pkt->pkt.pubkey_enc = cdk_calloc (1, sizeof *pkt->pkt.pubkey_enc); if (!pkt->pkt.pubkey_enc) return CDK_Out_Of_Core; rc = read_pubkey_enc (inp, pktlen, pkt->pkt.pubkey_enc); break; case CDK_PKT_COMPRESSED: pkt->pkt.compressed = cdk_calloc (1, sizeof *pkt->pkt.compressed); if (!pkt->pkt.compressed) return CDK_Out_Of_Core; rc = read_compressed (inp, pktlen, pkt->pkt.compressed); break; case CDK_PKT_MDC: pkt->pkt.mdc = cdk_calloc (1, sizeof *pkt->pkt.mdc); if (!pkt->pkt.mdc) return CDK_Out_Of_Core; rc = read_mdc (inp, pkt->pkt.mdc); break; default: /* Skip all packets we don't understand */ skip_packet (inp, pktlen); break; } return rc; }
static cdk_error_t read_secret_key (cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk) { size_t p1, p2, nread; int i, nskey; int rc; if (!inp || !sk || !sk->pk) return CDK_Inv_Value; if (DEBUG_PKT) _cdk_log_debug ("read_secret_key: %d octets\n", pktlen); p1 = cdk_stream_tell (inp); rc = read_public_key (inp, pktlen, sk->pk); if (rc) return rc; sk->s2k_usage = cdk_stream_getc (inp); sk->protect.sha1chk = 0; if (sk->s2k_usage == 254 || sk->s2k_usage == 255) { sk->protect.sha1chk = (sk->s2k_usage == 254); sk->protect.algo = _pgp_cipher_to_gnutls (cdk_stream_getc (inp)); sk->protect.s2k = cdk_calloc (1, sizeof *sk->protect.s2k); if (!sk->protect.s2k) return CDK_Out_Of_Core; rc = read_s2k (inp, sk->protect.s2k); if (rc) return rc; /* refer to --export-secret-subkeys in gpg(1) */ if (sk->protect.s2k->mode == CDK_S2K_GNU_EXT) sk->protect.ivlen = 0; else { sk->protect.ivlen = _gnutls_cipher_get_block_size (sk->protect.algo); if (!sk->protect.ivlen) return CDK_Inv_Packet; rc = stream_read (inp, sk->protect.iv, sk->protect.ivlen, &nread); if (rc) return rc; if (nread != sk->protect.ivlen) return CDK_Inv_Packet; } } else sk->protect.algo = _pgp_cipher_to_gnutls (sk->s2k_usage); if (sk->protect.algo == GNUTLS_CIPHER_NULL) { sk->csum = 0; nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); if (!nskey) { gnutls_assert (); return CDK_Inv_Algo; } for (i = 0; i < nskey; i++) { rc = read_mpi (inp, &sk->mpi[i], 1); if (rc) return rc; } sk->csum = read_16 (inp); sk->is_protected = 0; } else if (sk->pk->version < 4) { /* The length of each multiprecision integer is stored in plaintext. */ nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); if (!nskey) { gnutls_assert (); return CDK_Inv_Algo; } for (i = 0; i < nskey; i++) { rc = read_mpi (inp, &sk->mpi[i], 1); if (rc) return rc; } sk->csum = read_16 (inp); sk->is_protected = 1; } else { /* We need to read the rest of the packet because we do not have any information how long the encrypted mpi's are */ p2 = cdk_stream_tell (inp); p2 -= p1; sk->enclen = pktlen - p2; if (sk->enclen < 2) return CDK_Inv_Packet; /* at least 16 bits for the checksum! */ sk->encdata = cdk_calloc (1, sk->enclen + 1); if (!sk->encdata) return CDK_Out_Of_Core; if (stream_read (inp, sk->encdata, sk->enclen, &nread)) return CDK_Inv_Packet; /* Handle the GNU S2K extensions we know (just gnu-dummy right now): */ if (sk->protect.s2k->mode == CDK_S2K_GNU_EXT) { unsigned char gnumode; if ((sk->enclen < strlen ("GNU") + 1) || (0 != memcmp ("GNU", sk->encdata, strlen ("GNU")))) return CDK_Inv_Packet; gnumode = sk->encdata[strlen ("GNU")]; /* we only handle gnu-dummy (mode 1). mode 2 should refer to external smart cards. */ if (gnumode != 1) return CDK_Inv_Packet; /* gnu-dummy should have no more data */ if (sk->enclen != strlen ("GNU") + 1) return CDK_Inv_Packet; } nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); if (!nskey) { gnutls_assert (); return CDK_Inv_Algo; } /* We mark each MPI entry with NULL to indicate a protected key. */ for (i = 0; i < nskey; i++) sk->mpi[i] = NULL; sk->is_protected = 1; } sk->is_primary = 1; _cdk_copy_pk_to_sk (sk->pk, sk); return 0; }
/** * cdk_keydb_search: * @hd: the keydb object * @ks: the keydb search object * @ret_key: kbnode object to store the key * * Search for a key in the given keyring. The search mode is handled * via @ks. If the key was found, @ret_key contains the key data. **/ cdk_error_t cdk_keydb_search( cdk_keydb_hd_t hd, cdk_kbnode_t * ret_key ) { cdk_stream_t kr = NULL; cdk_kbnode_t knode = NULL; cdk_dbsearch_t ks; u32 off = 0; size_t pos = 0; int key_found = 0, cache_hit = 0; int rc = 0; if( !hd || !ret_key ) return CDK_Inv_Value; *ret_key = NULL; hd->search = 1; rc = cdk_keydb_open( hd, &kr ); if( rc ) return rc; rc = keydb_pos_from_cache( hd, hd->dbs, &cache_hit, &off ); if( rc ) return rc; ks = hd->dbs; while( !key_found && !rc ) { if( cache_hit && ks->type != CDK_DBSEARCH_NEXT ) cdk_stream_seek( kr, off ); pos = cdk_stream_tell( kr ); rc = cdk_keydb_get_keyblock( kr, &knode ); if( rc ) { if( rc == CDK_EOF && knode ) rc = 0; if( !knode && rc == CDK_EOF ) rc = CDK_Error_No_Key; if( rc ) break; } switch( ks->type ) { case CDK_DBSEARCH_SHORT_KEYID: case CDK_DBSEARCH_KEYID: key_found = find_by_keyid( knode, ks ); break; case CDK_DBSEARCH_FPR: key_found = find_by_fpr( knode, ks ); break; case CDK_DBSEARCH_EXACT: case CDK_DBSEARCH_SUBSTR: key_found = find_by_pattern( knode, ks ); break; case CDK_DBSEARCH_NEXT: key_found = knode? 1 : 0; break; } if( key_found ) { if( !keydb_cache_find( hd->cache, ks ) ) keydb_cache_add( hd, ks, pos ); break; } cdk_kbnode_release( knode ); knode = NULL; } hd->search = 0; *ret_key = key_found? knode : NULL; return rc; }
cdk_error_t cdk_keydb_get_keyblock( cdk_stream_t inp, cdk_kbnode_t * r_knode ) { cdk_packet_t pkt = NULL; cdk_kbnode_t knode = NULL, node = NULL; cdk_desig_revoker_t revkeys = NULL; u32 keyid[2], main_keyid[2]; int rc = 0, old_off; int key_seen = 0, got_key = 0; if( !inp || !r_knode ) return CDK_Inv_Value; memset( keyid, 0, sizeof keyid ); memset( main_keyid, 0, sizeof main_keyid ); while( 1 ) { pkt = cdk_calloc( 1, sizeof *pkt ); if( !pkt ) return CDK_Out_Of_Core; old_off = cdk_stream_tell( inp ); rc = cdk_pkt_read( inp, pkt ); if( rc ) break; if( pkt->pkttype == CDK_PKT_PUBLIC_KEY || pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY || pkt->pkttype == CDK_PKT_SECRET_KEY || pkt->pkttype == CDK_PKT_SECRET_SUBKEY) { if (key_seen && (pkt->pkttype == CDK_PKT_PUBLIC_KEY || pkt->pkttype == CDK_PKT_SECRET_KEY) ) { cdk_stream_seek( inp, old_off ); break; } if( pkt->pkttype == CDK_PKT_PUBLIC_KEY || pkt->pkttype == CDK_PKT_SECRET_KEY ) { _cdk_pkt_get_keyid( pkt, main_keyid ); key_seen = 1; } else if( pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY || pkt->pkttype == CDK_PKT_SECRET_SUBKEY ) { if( pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY ) { pkt->pkt.public_key->main_keyid[0] = main_keyid[0]; pkt->pkt.public_key->main_keyid[1] = main_keyid[1]; } else { pkt->pkt.secret_key->main_keyid[0] = main_keyid[0]; pkt->pkt.secret_key->main_keyid[1] = main_keyid[1]; } } /* we save this for the signature */ _cdk_pkt_get_keyid( pkt, keyid ); got_key = 1; } else if( pkt->pkttype == CDK_PKT_USER_ID ) ; else if( pkt->pkttype == CDK_PKT_SIGNATURE ) { pkt->pkt.signature->key[0] = keyid[0]; pkt->pkt.signature->key[1] = keyid[1]; if( pkt->pkt.signature->sig_class == 0x1F && pkt->pkt.signature->revkeys ) revkeys = pkt->pkt.signature->revkeys; } node = cdk_kbnode_new( pkt ); if( !knode ) knode = node; else _cdk_kbnode_add( knode, node ); } if( got_key ) { keydb_merge_selfsig( knode, main_keyid ); rc = keydb_parse_allsigs( knode, NULL, 0 ); if( revkeys ) { node = cdk_kbnode_find( knode, CDK_PKT_PUBLIC_KEY ); if( node ) node->pkt->pkt.public_key->revkeys = revkeys; } } *r_knode = got_key ? knode : NULL; return rc; }