/** * \ingroup hashdblib * Adds a new entry to a hash database. * @param hdb_info The hash database object * @param filename Name of the file that was hashed (can be NULL) * @param md5 Text representation of MD5 hash (can be NULL) * @param sha1 Text representation of SHA1 hash (can be NULL) * @param sha256 Text representation of SHA256 hash (can be NULL) * @param comment A comment to asociate with the hash (can be NULL) * @return 1 on error, 0 on success */ uint8_t tsk_hdb_add_entry(TSK_HDB_INFO *hdb_info, const char *filename, const char *md5, const char *sha1, const char *sha256, const char *comment) { const char *func_name = "tsk_hdb_add_entry"; if (!hdb_info) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr("%s: NULL hdb_info", func_name); return 1; } if (!hdb_info->add_entry) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr("%s: NULL add_entry function ptr", func_name); return 1; } if (hdb_info->accepts_updates()) { return hdb_info->add_entry(hdb_info, filename, md5, sha1, sha256, comment); } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_PROC); tsk_error_set_errstr("%s: operation not supported for this database type (=%u)", func_name, hdb_info->db_type); return 1; } }
/** * \internal * Return the a_idx'th attribute in the attribute list. * * @param a_fs_attrlist Data list structure to search in * @param a_idx 0-based index of attribute to return * * @return NULL is returned on error and if an entry could not be found. * tsk_errno will be set to TSK_ERR_FS_ATTR_NOTFOUND if entry could not be found. */ const TSK_FS_ATTR * tsk_fs_attrlist_get_idx(const TSK_FS_ATTRLIST * a_fs_attrlist, int a_idx) { TSK_FS_ATTR *fs_attr_cur; int i = 0; if (!a_fs_attrlist) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_attrlist_get_idx: Null list pointer"); return NULL; } for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; fs_attr_cur = fs_attr_cur->next) { if (fs_attr_cur->flags & TSK_FS_ATTR_INUSE) { if (i == a_idx) { return fs_attr_cur; } i++; } } tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND); tsk_error_set_errstr ("tsk_fs_attrlist_get_idx: Attribute index %d not found", a_idx); return NULL; }
/** * \internal * Return either an empty element in the list or create a new one at the end * * Preference is given to finding one of the same type to prevent * excessive malloc's, but if one is not found then a different * type is used: type = [TSK_FS_ATTR_NONRES | TSK_FS_ATTR_RES] * * @param a_fs_attrlist Attribute list to search * @param a_atype Preference for attribute type to reuse * @return NULL on error or attribute in list to use */ TSK_FS_ATTR * tsk_fs_attrlist_getnew(TSK_FS_ATTRLIST * a_fs_attrlist, TSK_FS_ATTR_FLAG_ENUM a_atype) { TSK_FS_ATTR *fs_attr_cur; TSK_FS_ATTR *fs_attr_ok = NULL; if (a_fs_attrlist == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Null list in tsk_fs_attrlist_getnew()"); return NULL; } if ((a_atype != TSK_FS_ATTR_NONRES) && (a_atype != TSK_FS_ATTR_RES)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Invalid Type in tsk_fs_attrlist_getnew()"); return NULL; } for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; fs_attr_cur = fs_attr_cur->next) { if (fs_attr_cur->flags == 0) { if (a_atype == TSK_FS_ATTR_NONRES) { if (fs_attr_cur->nrd.run) break; else if (!fs_attr_ok) fs_attr_ok = fs_attr_cur; } /* we want one with an allocated buf */ else { if (fs_attr_cur->rd.buf_size) break; else if (!fs_attr_ok) fs_attr_ok = fs_attr_cur; } } } /* if we fell out then check fs_attr_tmp */ if (!fs_attr_cur) { if (fs_attr_ok) fs_attr_cur = fs_attr_ok; else { /* make a new one */ if ((fs_attr_cur = tsk_fs_attr_alloc(a_atype)) == NULL) return NULL; // add it to the list if (tsk_fs_attrlist_add(a_fs_attrlist, fs_attr_cur)) { tsk_fs_attr_free(fs_attr_cur); return NULL; } } } fs_attr_cur->flags = (TSK_FS_ATTR_INUSE | a_atype); return fs_attr_cur; }
/** * \internal * Search the attribute list of TSK_FS_ATTR structures for an entry with a given * type and id. * * @param a_fs_attrlist Data list structure to search in * @param a_type Type of attribute to find * @param a_id Id of attribute to find. * * @return NULL is returned on error and if an entry could not be found. * tsk_errno will be set to TSK_ERR_FS_ATTR_NOTFOUND if entry could not be found. */ const TSK_FS_ATTR * tsk_fs_attrlist_get_id(const TSK_FS_ATTRLIST * a_fs_attrlist, TSK_FS_ATTR_TYPE_ENUM a_type, uint16_t a_id) { TSK_FS_ATTR *fs_attr_cur; if (!a_fs_attrlist) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_attrlist_get_id: Null list pointer"); return NULL; } for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; fs_attr_cur = fs_attr_cur->next) { if ((fs_attr_cur->flags & TSK_FS_ATTR_INUSE) && (fs_attr_cur->type == a_type) && (fs_attr_cur->id == a_id)) return fs_attr_cur; } tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND); tsk_error_set_errstr ("tsk_fs_attrlist_get_id: Attribute %d-%d not found", a_type, a_id); return NULL; }
/** * Walk through the Update Sequence Number journal file * opened with ntfs_usnjopen. * * For each USN record, calls the callback action passing the USN record header, * the USN record and the pointer ptr. * * @param ntfs File system where the journal is stored * @param action action to be called per each USN entry * @param ptr pointer to data passed to the action callback * @returns 0 on success, 1 otherwise */ uint8_t tsk_ntfs_usnjentry_walk(TSK_FS_INFO *fs, TSK_FS_USNJENTRY_WALK_CB action, void *ptr) { uint8_t ret = 0; unsigned char *buf = NULL; NTFS_INFO *ntfs = (NTFS_INFO*)fs; tsk_error_reset(); if (ntfs == NULL || ntfs->fs_info.ftype != TSK_FS_TYPE_NTFS) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Invalid FS type in ntfs_usnjentry_walk"); return 1; } if (ntfs->usnjinfo == NULL) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("Must call tsk_ntfs_usnjopen first"); return 1; } buf = tsk_malloc(ntfs->usnjinfo->bsize); if (buf == NULL) return 1; ret = parse_file(ntfs, buf, action, ptr); tsk_fs_file_close(ntfs->usnjinfo->fs_file); free(ntfs->usnjinfo); free(buf); return ret; }
/** * \ingroup fslib * Read arbitrary data from inside of the file system. * @param a_fs The file system handle. * @param a_off The byte offset to start reading from (relative to start of file system) * @param a_buf The buffer to store the block in. * @param a_len The number of bytes to read * @return The number of bytes read or -1 on error. */ ssize_t tsk_fs_read(TSK_FS_INFO * a_fs, TSK_OFF_T a_off, char *a_buf, size_t a_len) { // do a sanity check on the read bounds, but only if the block // value has been set. // note that this could prevent us from viewing the FS slack... if ((a_fs->last_block_act > 0) && ((TSK_DADDR_T) a_off >= ((a_fs->last_block_act + 1) * a_fs->block_size))) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); if ((TSK_DADDR_T) a_off < ((a_fs->last_block + 1) * a_fs->block_size)) tsk_error_set_errstr ("tsk_fs_read: Offset missing in partial image: %" PRIuDADDR ")", a_off); else tsk_error_set_errstr ("tsk_fs_read: Offset is too large for image: %" PRIuDADDR ")", a_off); return -1; } if (((a_fs->block_pre_size) || (a_fs->block_post_size)) && (a_fs->block_size)) { return fs_prepost_read(a_fs, a_off, a_buf, a_len); } else { return tsk_img_read(a_fs->img_info, a_off + a_fs->offset, a_buf, a_len); } }
/** \internal * Check the arguments for the tsk_fs_file_attr_XXX functions * and load the attributes if needed. * @param a_fs_file File argument to check. * @param a_func Name of function that this is checking for (for error messages) * @returns 1 on error */ static int tsk_fs_file_attr_check(TSK_FS_FILE * a_fs_file, char *a_func) { TSK_FS_INFO *fs; // check the FS_INFO, FS_FILE structures if ((a_fs_file == NULL) || (a_fs_file->meta == NULL) || (a_fs_file->fs_info == NULL)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("%s: called with NULL pointers", a_func); return 1; } else if (a_fs_file->meta->tag != TSK_FS_META_TAG) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("%s: called with unallocated structures", a_func); return 1; } fs = a_fs_file->fs_info; // If the attributes haven't been loaded, then load them. if (a_fs_file->meta->attr_state == TSK_FS_META_ATTR_ERROR) { tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr("%s: called for file with corrupt data", a_func); return 1; } else if ((a_fs_file->meta->attr_state != TSK_FS_META_ATTR_STUDIED) || (a_fs_file->meta->attr == NULL)) { if (fs->load_attrs(a_fs_file)) { return 1; } } return 0; }
/** * \ingroup vslib * Return handle to a volume in the volume system. * * @param a_vs Open volume system * @param a_idx Index for volume to return (0-based) * @returns Handle to volume or NULL on error */ const TSK_VS_PART_INFO * tsk_vs_part_get(const TSK_VS_INFO * a_vs, TSK_PNUM_T a_idx) { TSK_VS_PART_INFO *part; if ((a_vs == NULL) || (a_vs->tag != TSK_VS_INFO_TAG)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_ARG); tsk_error_set_errstr ("tsk_vs_part_get: pointer is NULL or has unallocated structures"); return NULL; } if (a_idx >= a_vs->part_count) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_ARG); tsk_error_set_errstr("tsk_vs_part_get: Volume address is too big"); return NULL; } for (part = a_vs->part_list; part != NULL; part = part->next) { if (part->addr == a_idx) return part; } return NULL; }
/** * \ingroup fslib * Read the contents of a specific attribute of a file using a typical read() type interface and be * able specify a specific attribute to read (applies only to file systems with multiple attributes * per file, such as NTFS). 0s are returned for missing runs of files. * * @param a_fs_file The file to read from * @param a_type The type of attribute to load * @param a_id The id of attribute to load (use 0 and set a_flags if you do not care) * @param a_offset The byte offset to start reading from. * @param a_buf The buffer to read the data into. * @param a_len The number of bytes to read from the file. * @param a_flags Flags to use while reading * @returns The number of bytes read or -1 on error (incl if offset is past EOF). */ ssize_t tsk_fs_file_read_type(TSK_FS_FILE * a_fs_file, TSK_FS_ATTR_TYPE_ENUM a_type, uint16_t a_id, TSK_OFF_T a_offset, char *a_buf, size_t a_len, TSK_FS_FILE_READ_FLAG_ENUM a_flags) { const TSK_FS_ATTR *fs_attr; // clean up any error messages that are lying around tsk_error_reset(); // check the FS_INFO, FS_FILE structures if ((a_fs_file == NULL) || (a_fs_file->meta == NULL) || (a_fs_file->fs_info == NULL)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_file_read: called with NULL pointers"); return -1; } else if ((a_fs_file->fs_info->tag != TSK_FS_INFO_TAG) || (a_fs_file->meta->tag != TSK_FS_META_TAG)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_file_read: called with unallocated structures"); return -1; } if ((fs_attr = tsk_fs_file_attr_get_type(a_fs_file, a_type, a_id, (a_flags & TSK_FS_FILE_READ_FLAG_NOID) ? 0 : 1)) == NULL) { return -1; } return tsk_fs_attr_read(fs_attr, a_offset, a_buf, a_len, a_flags); }
static ssize_t ewf_image_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len) { ssize_t cnt; IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *) img_info; if (tsk_verbose) tsk_fprintf(stderr, "ewf_image_read: byte offset: %" PRIuOFF " len: %" PRIuSIZE "\n", offset, len); if (offset > img_info->size) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ_OFF); tsk_error_set_errstr("ewf_image_read - %" PRIuOFF, offset); return -1; } cnt = libewf_read_random(ewf_info->handle, buf, len, offset); if (cnt < 0) { tsk_error_reset(); // @@@ Add more specific error message tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ); tsk_error_set_errstr("ewf_image_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s", offset, len, strerror(errno)); return -1; } return cnt; }
int8_t tsk_hdb_lookup_verbose_str(TSK_HDB_INFO *hdb_info, const char *hash, void *result) { if (!hdb_info) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr("tsk_hdb_lookup_verbose_str: NULL hdb_info"); return -1; } if (!hash) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr("tsk_hdb_lookup_verbose_str: NULL hash"); return -1; } if (!result) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr("tsk_hdb_lookup_verbose_str: NULL result"); return -1; } return hdb_info->lookup_verbose_str(hdb_info, hash, result); }
/** \ingroup fslib * Return a specific file or subdirectory from an open directory. * @param a_fs_dir Directory to analyze * @param a_idx Index of file in directory to open (0-based) * @returns NULL on error */ TSK_FS_FILE * tsk_fs_dir_get(const TSK_FS_DIR * a_fs_dir, size_t a_idx) { TSK_FS_NAME *fs_name; TSK_FS_FILE *fs_file; if ((a_fs_dir == NULL) || (a_fs_dir->tag != TSK_FS_DIR_TAG) || (a_fs_dir->fs_info == NULL)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_dir_get: called with NULL or unallocated structures"); return NULL; } if (a_fs_dir->names_used <= a_idx) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_dir_get: Index (%" PRIuSIZE ") too large (%" PRIuSIZE ")", a_idx, a_fs_dir->names_used); return NULL; } // allocate a structure to return if ((fs_file = tsk_fs_file_alloc(a_fs_dir->fs_info)) == NULL) return NULL; fs_name = &(a_fs_dir->names[a_idx]); // copy the name into another structure that we can return and later free if ((fs_file->name = tsk_fs_name_alloc(fs_name->name ? strlen(fs_name->name) + 1 : 0, fs_name->shrt_name ? strlen(fs_name->shrt_name) + 1 : 0)) == NULL) { return NULL; } if (tsk_fs_name_copy(fs_file->name, fs_name)) return NULL; /* load the fs_meta structure if possible. * Must have non-zero inode addr or have allocated name (if inode is 0) */ if (((fs_name->meta_addr) || (fs_name->flags & TSK_FS_NAME_FLAG_ALLOC))) { if (a_fs_dir->fs_info->file_add_meta(a_fs_dir->fs_info, fs_file, fs_name->meta_addr)) { if (tsk_verbose) tsk_error_print(stderr); tsk_error_reset(); } // if the sequence numbers don't match, then don't load the meta // should ideally have sequence in previous lookup, but it isn't // in all APIs yet if (fs_file->meta->seq != fs_name->meta_seq) { tsk_fs_meta_close(fs_file->meta); fs_file->meta = NULL; } } return fs_file; }
/** * \ingroup fslib * Get the contents and flags of a specific file system block. Note that if the block contains * compressed data, then this function will return the compressed data with the RAW flag set. * The uncompressed data can be obtained only from the file-level functions. * * @param a_fs The file system to read the block from. * @param a_fs_block The structure to write the block data into or NULL to have one created. * @param a_addr The file system address to read. * @param a_flags Flag to assign to the returned TSK_FS_BLOCK (use if you already have it as part of a block_walk-type scenario) * @return The TSK_FS_BLOCK with the data or NULL on error. (If a_fs_block was not NULL, this will * be the same structure). */ TSK_FS_BLOCK * tsk_fs_block_get_flag(TSK_FS_INFO * a_fs, TSK_FS_BLOCK * a_fs_block, TSK_DADDR_T a_addr, TSK_FS_BLOCK_FLAG_ENUM a_flags) { TSK_OFF_T offs; size_t len; if (a_fs == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); tsk_error_set_errstr("tsk_fs_block_get: fs unallocated"); return NULL; } if (a_fs_block == NULL) { a_fs_block = tsk_fs_block_alloc(a_fs); } else if ((a_fs_block->tag != TSK_FS_BLOCK_TAG) || (a_fs_block->buf == NULL)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); tsk_error_set_errstr("tsk_fs_block_get: fs_block unallocated"); return NULL; } len = a_fs->block_size; if (a_addr > a_fs->last_block_act) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_READ); if (a_addr <= a_fs->last_block) tsk_error_set_errstr ("tsk_fs_block_get: Address missing in partial image: %" PRIuDADDR ")", a_addr); else tsk_error_set_errstr ("tsk_fs_block_get: Address is too large for image: %" PRIuDADDR ")", a_addr); return NULL; } a_fs_block->fs_info = a_fs; a_fs_block->addr = a_addr; a_fs_block->flags = a_flags; a_fs_block->flags |= TSK_FS_BLOCK_FLAG_RAW; offs = (TSK_OFF_T) a_addr *a_fs->block_size; if ((a_fs_block->flags & TSK_FS_BLOCK_FLAG_AONLY) == 0) { ssize_t cnt; cnt = tsk_img_read(a_fs->img_info, a_fs->offset + offs, a_fs_block->buf, len); if (cnt != (ssize_t)len) { return NULL; } } return a_fs_block; }
static ssize_t ewf_image_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len) { #if defined( HAVE_LIBEWF_V2_API ) char error_string[TSK_EWF_ERROR_STRING_SIZE]; libewf_error_t *ewf_error = NULL; #endif ssize_t cnt; IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *) img_info; if (tsk_verbose) tsk_fprintf(stderr, "ewf_image_read: byte offset: %" PRIuOFF " len: %" PRIuSIZE "\n", offset, len); if (offset > img_info->size) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ_OFF); tsk_error_set_errstr("ewf_image_read - %" PRIuOFF, offset); return -1; } tsk_take_lock(&(ewf_info->read_lock)); #if defined( HAVE_LIBEWF_V2_API ) cnt = libewf_handle_read_random(ewf_info->handle, buf, len, offset, &ewf_error); if (cnt < 0) { char *errmsg = NULL; tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ); if (getError(ewf_error, error_string)) errmsg = strerror(errno); else errmsg = error_string; tsk_error_set_errstr("ewf_image_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s", offset, len, errmsg); tsk_release_lock(&(ewf_info->read_lock)); return -1; } #else cnt = libewf_read_random(ewf_info->handle, buf, len, offset); if (cnt < 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ); tsk_error_set_errstr("ewf_image_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s", offset, len, strerror(errno)); tsk_release_lock(&(ewf_info->read_lock)); return -1; } #endif tsk_release_lock(&(ewf_info->read_lock)); return cnt; }
/** * \internal * Parses the MBR of an exFAT file system to obtain file system size * information - bytes per sector, sectors per cluster, and sectors per FAT - * to add to a FATFS_INFO object. * * @param [in, out] a_fatfs Generic FAT file system info structure. * @return 0 on success, 1 otherwise, per TSK convention. */ static uint8_t exfatfs_get_fs_size_params(FATFS_INFO *a_fatfs) { const char *func_name = "exfatfs_get_fs_size_params"; TSK_FS_INFO *fs = &(a_fatfs->fs_info); EXFATFS_MASTER_BOOT_REC *exfatbs = NULL; assert(a_fatfs != NULL); exfatbs = (EXFATFS_MASTER_BOOT_REC*)(&a_fatfs->boot_sector_buffer); /* Get bytes per sector. * Bytes per sector is a base 2 logarithm, defining a range of sizes with * a min of 512 bytes and a max of 4096 bytes. */ a_fatfs->ssize_sh = (uint16_t)exfatbs->bytes_per_sector; if ((a_fatfs->ssize_sh < 9) || (a_fatfs->ssize_sh > 12)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr("Not an exFAT file system (invalid sector size)"); if (tsk_verbose) { fprintf(stderr, "%s: Invalid sector size base 2 logarithm (%d), not in range (9 - 12)\n", func_name, a_fatfs->ssize); } return FATFS_FAIL; } a_fatfs->ssize = (1 << a_fatfs->ssize_sh); /* Get sectors per cluster. * Sectors per cluster is a base 2 logarithm. The max cluster size is * 32 MiB, so the sum of the bytes per sector and sectors per cluster * logs cannot exceed 25. */ if ((a_fatfs->ssize_sh + exfatbs->sectors_per_cluster) > 25) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr("Not an exFAT file system (invalid cluster size)"); if (tsk_verbose) { fprintf(stderr, "%s: Invalid cluster size (%d)\n", func_name, exfatbs->sectors_per_cluster); } return FATFS_FAIL; } a_fatfs->csize = (1 << exfatbs->sectors_per_cluster); /* Get sectors per FAT. * It will at least be non-zero. */ a_fatfs->sectperfat = tsk_getu32(fs->endian, exfatbs->fat_len_in_sectors); if (a_fatfs->sectperfat == 0) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); tsk_error_set_errstr("Not an exFAT file system (invalid sectors per FAT)"); if (tsk_verbose) { fprintf(stderr, "%s: Invalid number of sectors per FAT (%d)\n", func_name, a_fatfs->sectperfat); } return FATFS_FAIL; } return FATFS_OK; }
/* Note: The routine -assumes- we are under a lock on &(img_info->cache_lock)) */ static ssize_t aff_read(TSK_IMG_INFO * img_info, TSK_OFF_T offset, char *buf, size_t len) { ssize_t cnt; IMG_AFF_INFO *aff_info = (IMG_AFF_INFO *) img_info; if (tsk_verbose) tsk_fprintf(stderr, "aff_read: byte offset: %" PRIuOFF " len: %" PRIuOFF "\n", offset, len); if (offset > img_info->size) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ_OFF); tsk_error_set_errstr("aff_read - %" PRIuOFF, offset); return -1; } if (aff_info->seek_pos != offset) { if (af_seek(aff_info->af_file, offset, SEEK_SET) != offset) { tsk_error_reset(); // @@@ ADD more specific error messages tsk_error_set_errno(TSK_ERR_IMG_SEEK); tsk_error_set_errstr("aff_read - %" PRIuOFF " - %s", offset, strerror(errno)); return -1; } aff_info->seek_pos = offset; } cnt = af_read(aff_info->af_file, (unsigned char *) buf, len); if (cnt < 0) { // @@@ Add more specific error message tsk_error_reset(); tsk_error_set_errno(TSK_ERR_IMG_READ); tsk_error_set_errstr("aff_read - offset: %" PRIuOFF " - len: %" PRIuSIZE " - %s", offset, len, strerror(errno)); return -1; } /* AFF will return 0 if the page does not exist -- fill the * buffer with zeros in this case */ if (cnt == 0) { // @@@ We could improve this if there is an AFF call // to see if the data exists or not if ((af_eof(aff_info->af_file) == 0) && (offset + len < img_info->size)) { memset(buf, 0, len); cnt = len; } } aff_info->seek_pos += cnt; return cnt; }
/** * @param db_path Path to DB, which probably does not exist. But it gets passed in because we need * it in a bunch of places. * @param idx_path Path to index file (should be superset of db_path) */ TSK_HDB_INFO *idxonly_open(const TSK_TCHAR *db_path, const TSK_TCHAR *idx_path) { TSK_HDB_BINSRCH_INFO *hdb_binsrch_info = NULL; TSK_TCHAR *ext; TSK_HDB_HTYPE_ENUM htype; hdb_binsrch_info = hdb_binsrch_open(NULL, db_path); if (NULL == hdb_binsrch_info) { return NULL; } hdb_binsrch_info->base.db_type = TSK_HDB_DBTYPE_IDXONLY_ID; // open the index ext = TSTRRCHR(idx_path, _TSK_T('-')); if (ext == NULL) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr("idxonly_open: invalid file name (no extension): %" PRIttocTSK, idx_path); return NULL; } else if ((TSTRLEN(ext) == 8) && (TSTRICMP(ext, _TSK_T("-md5.idx")) == 0)) { htype = TSK_HDB_HTYPE_MD5_ID; } else if ((TSTRLEN(ext) == 9) && (TSTRICMP(ext, _TSK_T("-sha1.idx")) == 0)) { htype = TSK_HDB_HTYPE_SHA1_ID; } else { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr("idxonly_open: invalid file name (unknown extension): %" PRIttocTSK, idx_path); return NULL; } if (hdb_binsrch_open_idx((TSK_HDB_INFO*)hdb_binsrch_info, htype)) { return NULL; } if (idxonly_name(hdb_binsrch_info)) { hdb_binsrch_close((TSK_HDB_INFO*)hdb_binsrch_info); return NULL; } hdb_binsrch_info->base.get_db_path = idxonly_get_db_path; hdb_binsrch_info->get_entry = idxonly_getentry; // Before returning, do one final check that we'll be able to open // the index file if (hdb_binsrch_open_idx((TSK_HDB_INFO*)hdb_binsrch_info, hdb_binsrch_info->hash_type)) { hdb_binsrch_close((TSK_HDB_INFO*)hdb_binsrch_info); return NULL; } return (TSK_HDB_INFO*)hdb_binsrch_info; }
/** * \ingroup fslib * Process an attribute and call a callback function with its contents. The callback will be * called with chunks of data that are fs->block_size or less. The address given in the callback * will be correct only for raw files (when the raw file contents were stored in the block). For * compressed and sparse attributes, the address may be zero. * * @param a_fs_attr Attribute to process * @param a_flags Flags to use while processing attribute * @param a_action Callback action to call with content * @param a_ptr Pointer that will passed to callback * @returns 1 on error and 0 on success. */ uint8_t tsk_fs_attr_walk(const TSK_FS_ATTR * a_fs_attr, TSK_FS_FILE_WALK_FLAG_ENUM a_flags, TSK_FS_FILE_WALK_CB a_action, void *a_ptr) { TSK_FS_INFO *fs; // clean up any error messages that are lying around tsk_error_reset(); // check the FS_INFO, FS_FILE structures if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL) || (a_fs_attr->fs_file->meta == NULL) || (a_fs_attr->fs_file->fs_info == NULL)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_walk: called with NULL pointers"); return 1; } fs = a_fs_attr->fs_file->fs_info; if (fs->tag != TSK_FS_INFO_TAG) { // || (a_fs_attr->id != TSK_FS_ATTR_ID)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_walk: called with unallocated structures"); return 1; } if (a_fs_attr->flags & TSK_FS_ATTR_COMP) { if (a_fs_attr->w == NULL) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_walk: compressed attribute found, but special function not defined"); return 1; } return a_fs_attr->w(a_fs_attr, a_flags, a_action, a_ptr); } // resident data if (a_fs_attr->flags & TSK_FS_ATTR_RES) { return tsk_fs_attr_walk_res(a_fs_attr, a_flags, a_action, a_ptr); } // non-resident data else if (a_fs_attr->flags & TSK_FS_ATTR_NONRES) { return tsk_fs_attr_walk_nonres(a_fs_attr, a_flags, a_action, a_ptr); } tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attr_walk: called with unknown attribute type: %x", a_fs_attr->flags); return 1; }
/** * \internal * Search the attribute list of TSK_FS_ATTR structures for an entry with a given * type (no ID) and a given name. If more than one entry with the same type exists, * the one with the lowest ID will be returned. * * @param a_fs_attrlist Data list structure to search in * @param a_type Type of attribute to find * @param name Name of the attribute to find (NULL for an entry with no name) * * @return NULL is returned on error and if an entry could not be found. * tsk_errno will be set to TSK_ERR_FS_ATTR_NOTFOUND if entry could not be found. */ const TSK_FS_ATTR * tsk_fs_attrlist_get_name_type(const TSK_FS_ATTRLIST * a_fs_attrlist, TSK_FS_ATTR_TYPE_ENUM a_type, const char *name) { TSK_FS_ATTR *fs_attr_cur; TSK_FS_ATTR *fs_attr_ok = NULL; if (!a_fs_attrlist) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_attrlist_get_name_type: Null list pointer"); return NULL; } for (fs_attr_cur = a_fs_attrlist->head; fs_attr_cur; fs_attr_cur = fs_attr_cur->next) { if ((fs_attr_cur->flags & TSK_FS_ATTR_INUSE) && (fs_attr_cur->type == a_type)) { if (((name == NULL) && (fs_attr_cur->name == NULL)) || ((name) && (fs_attr_cur->name) && (!strcmp(fs_attr_cur->name, name)))) { /* If we are looking for NTFS $Data, * then return default when we see it */ if ((fs_attr_cur->type == TSK_FS_ATTR_TYPE_NTFS_DATA) && (fs_attr_cur->name == NULL)) { return fs_attr_cur; } // make sure we return the lowest if multiple exist if ((fs_attr_ok == NULL) || (fs_attr_ok->id > fs_attr_cur->id)) fs_attr_ok = fs_attr_cur; } } } if (!fs_attr_ok) { tsk_error_set_errno(TSK_ERR_FS_ATTR_NOTFOUND); tsk_error_set_errstr("tsk_fs_attrlist_get: Attribute %d not found", a_type); return NULL; } else { return fs_attr_ok; } }
/** * \ingroup fslib * Return the handle structure for a specific file, given its full path. Note that * if you have the metadata address fo the file, then tsk_fs_file_open_meta() is a * more efficient approach. * * @param a_fs File system to analyze * @param a_fs_file Structure to store file data in or NULL to have one allocated. * @param a_path Path of file to open * @returns NULL on error */ TSK_FS_FILE * tsk_fs_file_open(TSK_FS_INFO * a_fs, TSK_FS_FILE * a_fs_file, const char *a_path) { TSK_INUM_T inum; int8_t retval; TSK_FS_FILE *fs_file = NULL; TSK_FS_NAME *fs_name = NULL; if ((a_fs == NULL) || (a_fs->tag != TSK_FS_INFO_TAG)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_file_open: called with NULL or unallocated structures"); return NULL; } // allocate a structure to store the name in if ((fs_name = tsk_fs_name_alloc(128, 32)) == NULL) { return NULL; } retval = tsk_fs_path2inum(a_fs, a_path, &inum, fs_name); if (retval == -1) { tsk_fs_name_free(fs_name); return NULL; } else if (retval == 1) { tsk_fs_name_free(fs_name); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_file_open: path not found: %s", a_path); return NULL; } fs_file = tsk_fs_file_open_meta(a_fs, a_fs_file, inum); if (fs_file) { // Add the name to the structure fs_file->name = fs_name; // path2inum did not put this in there... fs_name->meta_seq = fs_file->meta->seq; } else { tsk_fs_name_free(fs_name); } return fs_file; }
TSK_WALK_RET_ENUM hive_action(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr, char *buf, size_t size, TSK_FS_BLOCK_FLAG_ENUM flags, void *ptr) { if (size == 0){ return TSK_WALK_CONT; } FILE *fp = fopen("tmp", "ab+"); if(fp==NULL) { printf("file open fail\n"); exit(-1); } if (fwrite(buf, size, 1, fp) != 1) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_WRITE); tsk_error_set_errstr("icat_action: error writing to stdout: %s", strerror(errno)); fclose(fp); return TSK_WALK_ERROR; } fclose(fp); return TSK_WALK_CONT; }
/** * Starts in a specified byte offset of the opened disk images and looks for a * file system. Will call processFile() on each file * that is found. Same as findFilesInFs, but gives more detailed return values. * @param a_start Byte offset to start analyzing from. * @param a_ftype File system type. * @returns Error (messages will have been registered), OK, or STOP. */ TSK_RETVAL_ENUM TskAuto::findFilesInFsRet(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_ftype) { if (!m_img_info) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_NOTOPEN); tsk_error_set_errstr("findFilesInFsRet -- img_info"); registerError(); return TSK_ERR; } TSK_FS_INFO *fs_info; if ((fs_info = tsk_fs_open_img(m_img_info, a_start, a_ftype)) == NULL) { if (getCurVsPartFlag() & TSK_VS_PART_FLAG_ALLOC) { tsk_error_set_errstr2 ("Sector offset: %" PRIuOFF ", Partition Type: %s", a_start/512, getCurVsPartDescr().c_str() ); registerError(); return TSK_ERR; } else { tsk_error_reset(); return TSK_OK; } } TSK_RETVAL_ENUM retval = findFilesInFsInt(fs_info, fs_info->root_inum); tsk_fs_close(fs_info); if (m_errors.empty() == false) return TSK_ERR; else return retval; }
/** * Analyze the header line of the database to determine the version of NSRL * * @param str line from the database file * * @return version or -1 on error */ static int get_format_ver(char *str) { /* "SHA-1","FileName","FileSize","ProductCode","OpSystemCode","MD4","MD5","CRC32","SpecialCode" */ if ((str[9] == 'F') && (str[20] == 'F') && (str[24] == 'S') && (str[31] == 'P') && (str[45] == 'O')) return TSK_HDB_NSRL_FORM1; /* "SHA-1","MD5","CRC32","FileName","FileSize","ProductCode","OpSystemCode","Specia lCode" */ else if ((str[9] == 'M') && (str[15] == 'C') && (str[23] == 'F') && (str[34] == 'F') && (str[45] == 'P')) return TSK_HDB_NSRL_FORM2; tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); tsk_error_set_errstr( "nsrl: Unknown header format: %s\n", str); return -1; }
/** * Creates a new multi-user case with a new database and initializes its tables. * Fails if multi-user database with requested name already exists. * * @param path Full path to create new database at. * @returns Pointer to a new TskCaseDb object, NULL on error */ TskCaseDb * TskCaseDb::newDb(const TSK_TCHAR * const path, CaseDbConnectionInfo * info) { #if defined(HAVE_POSTGRESQL) && defined(TSK_WIN32) TskDb *db = new TskDbPostgreSQL(path, true); // Store connection info for the multi-user database if (db->setConnectionInfo(info) != TSK_OK) { delete(db); return NULL; } // Check if the database already exsists if (db->dbExists()) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_DB); tsk_error_set_errstr("Database %" PRIttocTSK " already exists. Must be deleted first.", path); delete(db); return NULL; } // Open the database. if (db->open(true)) { delete(db); return NULL; } return new TskCaseDb(db); #else return NULL; #endif // HAVE_POSTGRESQL && TSK_WIN32 }
/** * Starts in a specified byte offset of the opened disk images and looks for a * file system. Will start processing the file system at a specified * file system. Will call processFile() on each file * that is found in that directory. * * @param a_start Byte offset of file system starting location. * @param a_ftype Type of file system that will be analyzed. * @param a_inum inum to start walking files system at. * * @returns 1 if an error occured (messages will have been registered) and 0 on success */ uint8_t TskAuto::findFilesInFs(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_ftype, TSK_INUM_T a_inum) { if (!m_img_info) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_NOTOPEN); tsk_error_set_errstr("findFilesInFs -- img_info "); registerError(); return 1; } TSK_FS_INFO *fs_info; if ((fs_info = tsk_fs_open_img(m_img_info, a_start, a_ftype)) == NULL) { if (getCurVsPartFlag() & TSK_VS_PART_FLAG_ALLOC) { tsk_error_set_errstr2( "Sector offset: %" PRIuOFF ", Partition Type: %s", a_start / 512, getCurVsPartDescr().c_str()); registerError(); return 1; } else { tsk_error_reset(); return 0; } } findFilesInFsInt(fs_info, a_inum); tsk_fs_close(fs_info); return m_errors.empty() ? 0 : 1; }
/** * file walk callback that is used to load directory contents * into a buffer */ static TSK_WALK_RET_ENUM fatfs_dent_action(TSK_FS_FILE * fs_file, TSK_OFF_T a_off, TSK_DADDR_T addr, char *buf, size_t size, TSK_FS_BLOCK_FLAG_ENUM flags, void *ptr) { FATFS_LOAD_DIR *load = (FATFS_LOAD_DIR *) ptr; /* how much of the buffer are we copying */ size_t len = (load->dirleft < size) ? load->dirleft : size; /* Copy the sector into a buffer and increment the pointers */ memcpy(load->curdirptr, buf, len); load->curdirptr = (char *) ((uintptr_t) load->curdirptr + len); load->dirleft -= len; /* fill in the stack of addresses of sectors * * if we are at the last entry, then realloc more */ if (load->addridx == load->addrsize) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("fatfs_dent_walk: Trying to put more sector address in stack than were allocated (%lu)", (long) load->addridx); return TSK_WALK_ERROR; } /* Add this sector to the stack */ load->addrbuf[load->addridx++] = addr; if (load->dirleft) return TSK_WALK_CONT; else return TSK_WALK_STOP; }
/* load a sparc disk label, this is called from the general * sun_load_table */ static uint8_t sun_load_table_sparc(TSK_VS_INFO * vs, sun_dlabel_sparc * dlabel_sp) { uint32_t idx = 0; uint32_t cyl_conv; TSK_DADDR_T max_addr = (vs->img_info->size - vs->offset) / vs->block_size; // max sector /* The value to convert cylinders to sectors */ cyl_conv = tsk_getu16(vs->endian, dlabel_sp->sec_per_tr) * tsk_getu16(vs->endian, dlabel_sp->num_head); if (tsk_verbose) tsk_fprintf(stderr, "load_table_sparc: Number of partitions: %d\n", tsk_getu16(vs->endian, dlabel_sp->num_parts)); /* Cycle through the partitions, there are either 8 or 16 */ for (idx = 0; idx < tsk_getu16(vs->endian, dlabel_sp->num_parts); idx++) { TSK_VS_PART_FLAG_ENUM ptype = TSK_VS_PART_FLAG_ALLOC; uint32_t part_start = cyl_conv * tsk_getu32(vs->endian, dlabel_sp->part_layout[idx].start_cyl); uint32_t part_size = tsk_getu32(vs->endian, dlabel_sp->part_layout[idx].size_blk); if (tsk_verbose) tsk_fprintf(stderr, "load_table_sparc: %" PRIu32 " Starting Sector: %" PRIu32 " Size: %" PRIu32 " Type: %" PRIu16 "\n", idx, part_start, part_size, tsk_getu16(vs->endian, dlabel_sp->part_meta[idx].type)); if (part_size == 0) continue; // make sure the first couple are in the image bounds if ((idx < 2) && (part_start > max_addr)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_BLK_NUM); tsk_error_set_errstr ("sun_load_sparc: Starting sector too large for image"); return 1; } // set the entry that covers the entire disk image as DESC if ((tsk_getu16(vs->endian, dlabel_sp->part_meta[idx].type) == 5) && (part_start == 0)) ptype = TSK_VS_PART_FLAG_META; /* Add the partition to the internal sorted list */ if (NULL == tsk_vs_part_add(vs, (TSK_DADDR_T) part_start, (TSK_DADDR_T) part_size, ptype, sun_get_desc(tsk_getu16(vs->endian, dlabel_sp->part_meta[idx].type)), -1, idx)) return 1; } return 0; }
/** * Starts in a specified byte offset of the opened disk images and looks for a * file system. Will start processing the file system at a specified * file system. Will call processFile() on each file * that is found in that directory. * * @param a_start Byte offset of file system starting location. * @param a_ftype Type of file system that will be analyzed. * @param a_inum inum to start walking files system at. * * @returns 1 on error and 0 on success */ uint8_t TskAuto::findFilesInFs(TSK_OFF_T a_start, TSK_FS_TYPE_ENUM a_ftype, TSK_INUM_T a_inum) { if (!m_img_info) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_NOTOPEN); tsk_error_set_errstr("findFilesInFs\n"); return 1; } TSK_FS_INFO *fs_info; /* Try it as a file system */ if ((fs_info = tsk_fs_open_img(m_img_info, a_start, a_ftype)) == NULL) { char msg[1024]; snprintf(msg, 1024, "Unable to open file system at offset %" PRIuOFF " (%s)", +a_start, tsk_error_get()); if (tsk_verbose) fprintf(stderr, "%s\n", msg); handleNotification(msg); /* We could do some carving on the volume data at this point */ return 1; } TSK_RETVAL_ENUM retval = findFilesInFsInt(fs_info, a_inum); tsk_fs_close(fs_info); if (retval == TSK_ERR) return 1; else return 0; }
/** * \ingroup vslib * Walk a range of partitions and pass the data to a callback function. * * @param a_vs Pointer to open volume system * @param a_start Address of first partition to walk from. * @param a_last Address of last partition to walk to. * @param a_flags Flags that are used to identify which of the partitions in the range should be returned (if 0, all partitions will be returned). * @param a_action Callback action to call for each partition. * @param a_ptr Pointer to data that will be passed to callback. * @returns 1 on error and 0 on success */ uint8_t tsk_vs_part_walk(TSK_VS_INFO * a_vs, TSK_PNUM_T a_start, TSK_PNUM_T a_last, TSK_VS_PART_FLAG_ENUM a_flags, TSK_VS_PART_WALK_CB a_action, void *a_ptr) { TSK_VS_PART_INFO *part; if (a_start >= a_vs->part_count) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_WALK_RNG); tsk_error_set_errstr ("tsk_vs_part_walk: Start partition too large: %" PRIuPNUM "", a_start); return 1; } if (a_last >= a_vs->part_count) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_VS_WALK_RNG); tsk_error_set_errstr("tsk_vs_part_walk: End partition too large: %" PRIuPNUM "", a_last); return 1; } if (a_flags == 0) { a_flags |= (TSK_VS_PART_FLAG_ALLOC | TSK_VS_PART_FLAG_UNALLOC | TSK_VS_PART_FLAG_META); } for (part = a_vs->part_list; part != NULL; part = part->next) { if ((part->addr >= a_start) && ((part->flags & a_flags) != 0)) { int retval; retval = a_action(a_vs, part, a_ptr); if (retval == TSK_WALK_STOP) { return 0; } else if (retval == TSK_WALK_ERROR) { return 1; } } if (part->addr >= a_last) break; } return 0; }
/** * Returns a string containing the md5 hash of the given file * * @param a_fs_file The file to calculate the hash of * @param a_hash_results The results will be stored here (must be allocated beforehand) * @param a_flags Indicates which hash algorithm(s) to use * @returns 0 on success or 1 on error */ extern uint8_t tsk_fs_file_hash_calc(TSK_FS_FILE * a_fs_file, TSK_FS_HASH_RESULTS * a_hash_results, TSK_BASE_HASH_ENUM a_flags) { TSK_FS_HASH_DATA hash_data; if ((a_fs_file == NULL) || (a_fs_file->fs_info == NULL) || (a_fs_file->meta == NULL)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_file_hash_calc: fs_info is NULL"); return 1; } if (a_hash_results == NULL) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr ("tsk_fs_file_hash_calc: hash_results is NULL"); return 1; } if (a_flags & TSK_BASE_HASH_MD5) { TSK_MD5_Init(&(hash_data.md5_context)); } if (a_flags & TSK_BASE_HASH_SHA1) { TSK_SHA_Init(&(hash_data.sha1_context)); } hash_data.flags = a_flags; if (tsk_fs_file_walk(a_fs_file, TSK_FS_FILE_WALK_FLAG_NONE, tsk_fs_file_hash_calc_callback, (void *) &hash_data)) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("tsk_fs_file_hash_calc: error in file walk"); return 1; } a_hash_results->flags = a_flags; if (a_flags & TSK_BASE_HASH_MD5) { TSK_MD5_Final(a_hash_results->md5_digest, &(hash_data.md5_context)); } if (a_flags & TSK_BASE_HASH_SHA1) { TSK_SHA_Final(a_hash_results->sha1_digest, &(hash_data.sha1_context)); } return 0; }