10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51061Scasper * Common Development and Distribution License (the "License"). 61061Scasper * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*2462Scasper * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/stat.h> 290Sstevel@tonic-gate #include <sys/types.h> 300Sstevel@tonic-gate 310Sstevel@tonic-gate #include <assert.h> 320Sstevel@tonic-gate #include <ctype.h> 330Sstevel@tonic-gate #include <errno.h> 341061Scasper #include <fcntl.h> 350Sstevel@tonic-gate #include <libintl.h> 360Sstevel@tonic-gate #include <libscf.h> 370Sstevel@tonic-gate #include <libuutil.h> 380Sstevel@tonic-gate #include <limits.h> 390Sstevel@tonic-gate #include <md5.h> 400Sstevel@tonic-gate #include <pthread.h> 410Sstevel@tonic-gate #include <stdio.h> 420Sstevel@tonic-gate #include <stdlib.h> 430Sstevel@tonic-gate #include <string.h> 440Sstevel@tonic-gate #include <strings.h> 451061Scasper #include <unistd.h> 460Sstevel@tonic-gate 470Sstevel@tonic-gate #include <manifest_hash.h> 480Sstevel@tonic-gate 490Sstevel@tonic-gate /* 500Sstevel@tonic-gate * Translate a file name to property name. Return an allocated string or NULL 510Sstevel@tonic-gate * if realpath() fails. 520Sstevel@tonic-gate */ 530Sstevel@tonic-gate char * 540Sstevel@tonic-gate mhash_filename_to_propname(const char *in) 550Sstevel@tonic-gate { 560Sstevel@tonic-gate char *out, *cp, *base; 570Sstevel@tonic-gate size_t len, piece_len; 580Sstevel@tonic-gate 590Sstevel@tonic-gate out = uu_zalloc(PATH_MAX + 1); 600Sstevel@tonic-gate if (realpath(in, out) == NULL) { 610Sstevel@tonic-gate uu_free(out); 620Sstevel@tonic-gate return (NULL); 630Sstevel@tonic-gate } 640Sstevel@tonic-gate 650Sstevel@tonic-gate base = getenv("PKG_INSTALL_ROOT"); 660Sstevel@tonic-gate 670Sstevel@tonic-gate /* 680Sstevel@tonic-gate * We copy-shift over the basedir and the leading slash, since it's 690Sstevel@tonic-gate * not relevant to when we boot with this repository. 700Sstevel@tonic-gate */ 710Sstevel@tonic-gate 720Sstevel@tonic-gate cp = out + ((base != NULL)? strlen(base) : 0); 730Sstevel@tonic-gate if (*cp == '/') 740Sstevel@tonic-gate cp++; 750Sstevel@tonic-gate (void) memmove(out, cp, strlen(cp) + 1); 760Sstevel@tonic-gate 770Sstevel@tonic-gate len = strlen(out); 780Sstevel@tonic-gate if (len > scf_limit(SCF_LIMIT_MAX_NAME_LENGTH)) { 790Sstevel@tonic-gate /* Use the first half and the second half. */ 800Sstevel@tonic-gate piece_len = (scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) - 3) / 2; 810Sstevel@tonic-gate 820Sstevel@tonic-gate (void) strncpy(out + piece_len, "__", 2); 830Sstevel@tonic-gate 840Sstevel@tonic-gate (void) memmove(out + piece_len + 2, out + (len - piece_len), 850Sstevel@tonic-gate piece_len + 1); 860Sstevel@tonic-gate } 870Sstevel@tonic-gate 880Sstevel@tonic-gate /* 890Sstevel@tonic-gate * Translate non-property characters to '_', first making sure that 900Sstevel@tonic-gate * we don't begin with '_'. 910Sstevel@tonic-gate */ 920Sstevel@tonic-gate 930Sstevel@tonic-gate if (!isalpha(*out)) 940Sstevel@tonic-gate *out = 'A'; 950Sstevel@tonic-gate 960Sstevel@tonic-gate for (cp = out + 1; *cp != '\0'; ++cp) { 970Sstevel@tonic-gate if (!(isalnum(*cp) || *cp == '_' || *cp == '-')) 980Sstevel@tonic-gate *cp = '_'; 990Sstevel@tonic-gate } 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate return (out); 1020Sstevel@tonic-gate } 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate int 1050Sstevel@tonic-gate mhash_retrieve_entry(scf_handle_t *hndl, const char *name, uchar_t *hash) 1060Sstevel@tonic-gate { 1070Sstevel@tonic-gate scf_scope_t *scope; 1080Sstevel@tonic-gate scf_service_t *svc; 1090Sstevel@tonic-gate scf_propertygroup_t *pg; 1100Sstevel@tonic-gate scf_property_t *prop; 1110Sstevel@tonic-gate scf_value_t *val; 1120Sstevel@tonic-gate ssize_t szret; 1130Sstevel@tonic-gate int result = 0; 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate /* 1160Sstevel@tonic-gate * In this implementation the hash for name is the opaque value of 1170Sstevel@tonic-gate * svc:/MHASH_SVC/:properties/name/MHASH_PROP 1180Sstevel@tonic-gate */ 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate if ((scope = scf_scope_create(hndl)) == NULL || 1210Sstevel@tonic-gate (svc = scf_service_create(hndl)) == NULL || 1220Sstevel@tonic-gate (pg = scf_pg_create(hndl)) == NULL || 1230Sstevel@tonic-gate (prop = scf_property_create(hndl)) == NULL || 1240Sstevel@tonic-gate (val = scf_value_create(hndl)) == NULL) { 1250Sstevel@tonic-gate result = -1; 1260Sstevel@tonic-gate goto out; 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate if (scf_handle_get_local_scope(hndl, scope) < 0) { 1300Sstevel@tonic-gate result = -1; 1310Sstevel@tonic-gate goto out; 1320Sstevel@tonic-gate } 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate if (scf_scope_get_service(scope, MHASH_SVC, svc) < 0) { 1350Sstevel@tonic-gate result = -1; 1360Sstevel@tonic-gate goto out; 1370Sstevel@tonic-gate } 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate if (scf_service_get_pg(svc, name, pg) != SCF_SUCCESS) { 1400Sstevel@tonic-gate result = -1; 1410Sstevel@tonic-gate goto out; 1420Sstevel@tonic-gate } 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate if (scf_pg_get_property(pg, MHASH_PROP, prop) != SCF_SUCCESS) { 1450Sstevel@tonic-gate result = -1; 1460Sstevel@tonic-gate goto out; 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 1500Sstevel@tonic-gate result = -1; 1510Sstevel@tonic-gate goto out; 1520Sstevel@tonic-gate } 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate szret = scf_value_get_opaque(val, hash, MHASH_SIZE); 1550Sstevel@tonic-gate if (szret < 0) { 1560Sstevel@tonic-gate result = -1; 1570Sstevel@tonic-gate goto out; 1580Sstevel@tonic-gate } 1590Sstevel@tonic-gate 1601061Scasper /* 1611061Scasper * Make sure that the old hash is returned with 1621061Scasper * remainder of the bytes zeroed. 1631061Scasper */ 1641061Scasper if (szret == MHASH_SIZE_OLD) { 1651061Scasper (void) memset(hash + MHASH_SIZE_OLD, 0, 1661061Scasper MHASH_SIZE - MHASH_SIZE_OLD); 1671061Scasper } else if (szret != MHASH_SIZE) { 1680Sstevel@tonic-gate scf_value_destroy(val); 1690Sstevel@tonic-gate result = -1; 1700Sstevel@tonic-gate goto out; 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate out: 1740Sstevel@tonic-gate (void) scf_value_destroy(val); 1750Sstevel@tonic-gate scf_property_destroy(prop); 1760Sstevel@tonic-gate scf_pg_destroy(pg); 1770Sstevel@tonic-gate scf_service_destroy(svc); 1780Sstevel@tonic-gate scf_scope_destroy(scope); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate return (result); 1810Sstevel@tonic-gate } 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate int 1840Sstevel@tonic-gate mhash_store_entry(scf_handle_t *hndl, const char *name, uchar_t *hash, 1850Sstevel@tonic-gate char **errstr) 1860Sstevel@tonic-gate { 1870Sstevel@tonic-gate scf_scope_t *scope = NULL; 1880Sstevel@tonic-gate scf_service_t *svc = NULL; 1890Sstevel@tonic-gate scf_propertygroup_t *pg = NULL; 1900Sstevel@tonic-gate scf_property_t *prop = NULL; 1910Sstevel@tonic-gate scf_value_t *val = NULL; 1920Sstevel@tonic-gate scf_transaction_t *tx = NULL; 1930Sstevel@tonic-gate scf_transaction_entry_t *e = NULL; 1940Sstevel@tonic-gate int ret, result = 0; 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate int i; 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate if ((scope = scf_scope_create(hndl)) == NULL || 1990Sstevel@tonic-gate (svc = scf_service_create(hndl)) == NULL || 2000Sstevel@tonic-gate (pg = scf_pg_create(hndl)) == NULL || 2010Sstevel@tonic-gate (prop = scf_property_create(hndl)) == NULL) { 2020Sstevel@tonic-gate if (errstr != NULL) 2030Sstevel@tonic-gate *errstr = gettext("Could not create scf objects"); 2040Sstevel@tonic-gate result = -1; 2050Sstevel@tonic-gate goto out; 2060Sstevel@tonic-gate } 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate if (scf_handle_get_local_scope(hndl, scope) != SCF_SUCCESS) { 2090Sstevel@tonic-gate if (errstr != NULL) 2100Sstevel@tonic-gate *errstr = gettext("Could not get local scope"); 2110Sstevel@tonic-gate result = -1; 2120Sstevel@tonic-gate goto out; 2130Sstevel@tonic-gate } 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate for (i = 0; i < 5; ++i) { 2160Sstevel@tonic-gate scf_error_t err; 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate if (scf_scope_get_service(scope, MHASH_SVC, svc) == 2190Sstevel@tonic-gate SCF_SUCCESS) 2200Sstevel@tonic-gate break; 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate if (scf_error() != SCF_ERROR_NOT_FOUND) { 2230Sstevel@tonic-gate if (errstr != NULL) 2240Sstevel@tonic-gate *errstr = gettext("Could not get manifest hash " 2250Sstevel@tonic-gate "service"); 2260Sstevel@tonic-gate result = -1; 2270Sstevel@tonic-gate goto out; 2280Sstevel@tonic-gate } 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate if (scf_scope_add_service(scope, MHASH_SVC, svc) == 2310Sstevel@tonic-gate SCF_SUCCESS) 2320Sstevel@tonic-gate break; 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate err = scf_error(); 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate if (err == SCF_ERROR_EXISTS) 2370Sstevel@tonic-gate /* Try again. */ 2380Sstevel@tonic-gate continue; 2390Sstevel@tonic-gate else if (err == SCF_ERROR_PERMISSION_DENIED) { 2400Sstevel@tonic-gate if (errstr != NULL) 2410Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 2420Sstevel@tonic-gate "permission denied.\n"); 2430Sstevel@tonic-gate result = -1; 2440Sstevel@tonic-gate goto out; 2450Sstevel@tonic-gate } 2460Sstevel@tonic-gate 2470Sstevel@tonic-gate if (errstr != NULL) 2480Sstevel@tonic-gate *errstr = gettext("Could not add manifest hash " 2490Sstevel@tonic-gate "service"); 2500Sstevel@tonic-gate result = -1; 2510Sstevel@tonic-gate goto out; 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate if (i == 5) { 2550Sstevel@tonic-gate if (errstr != NULL) 2560Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 2570Sstevel@tonic-gate "service addition contention.\n"); 2580Sstevel@tonic-gate result = -1; 2590Sstevel@tonic-gate goto out; 2600Sstevel@tonic-gate } 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate for (i = 0; i < 5; ++i) { 2630Sstevel@tonic-gate scf_error_t err; 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate if (scf_service_get_pg(svc, name, pg) == SCF_SUCCESS) 2660Sstevel@tonic-gate break; 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate if (scf_error() != SCF_ERROR_NOT_FOUND) { 2690Sstevel@tonic-gate if (errstr != NULL) 2700Sstevel@tonic-gate *errstr = gettext("Could not get service's " 2710Sstevel@tonic-gate "hash record)"); 2720Sstevel@tonic-gate result = -1; 2730Sstevel@tonic-gate goto out; 2740Sstevel@tonic-gate } 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate if (scf_service_add_pg(svc, name, MHASH_PG_TYPE, 2770Sstevel@tonic-gate MHASH_PG_FLAGS, pg) == SCF_SUCCESS) 2780Sstevel@tonic-gate break; 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate err = scf_error(); 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate if (err == SCF_ERROR_EXISTS) 2830Sstevel@tonic-gate /* Try again. */ 2840Sstevel@tonic-gate continue; 2850Sstevel@tonic-gate else if (err == SCF_ERROR_PERMISSION_DENIED) { 2860Sstevel@tonic-gate if (errstr != NULL) 2870Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 2880Sstevel@tonic-gate "permission denied.\n"); 2890Sstevel@tonic-gate result = -1; 2900Sstevel@tonic-gate goto out; 2910Sstevel@tonic-gate } 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate if (errstr != NULL) 2940Sstevel@tonic-gate *errstr = gettext("Could not store file hash"); 2950Sstevel@tonic-gate result = -1; 2960Sstevel@tonic-gate goto out; 2970Sstevel@tonic-gate } 2980Sstevel@tonic-gate if (i == 5) { 2990Sstevel@tonic-gate if (errstr != NULL) 3000Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 3010Sstevel@tonic-gate "property group addition contention.\n"); 3020Sstevel@tonic-gate result = -1; 3030Sstevel@tonic-gate goto out; 3040Sstevel@tonic-gate } 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate if ((e = scf_entry_create(hndl)) == NULL || 3070Sstevel@tonic-gate (val = scf_value_create(hndl)) == NULL) { 3080Sstevel@tonic-gate if (errstr != NULL) 3090Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 3100Sstevel@tonic-gate "permission denied.\n"); 3110Sstevel@tonic-gate result = -1; 3120Sstevel@tonic-gate goto out; 3130Sstevel@tonic-gate } 3140Sstevel@tonic-gate 3150Sstevel@tonic-gate ret = scf_value_set_opaque(val, hash, MHASH_SIZE); 3160Sstevel@tonic-gate assert(ret == SCF_SUCCESS); 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate tx = scf_transaction_create(hndl); 3190Sstevel@tonic-gate if (tx == NULL) { 3200Sstevel@tonic-gate if (errstr != NULL) 3210Sstevel@tonic-gate *errstr = gettext("Could not create transaction"); 3220Sstevel@tonic-gate result = -1; 3230Sstevel@tonic-gate goto out; 3240Sstevel@tonic-gate } 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate do { 3270Sstevel@tonic-gate if (scf_pg_update(pg) == -1) { 3280Sstevel@tonic-gate if (errstr != NULL) 3290Sstevel@tonic-gate *errstr = gettext("Could not update hash " 3300Sstevel@tonic-gate "entry"); 3310Sstevel@tonic-gate result = -1; 3320Sstevel@tonic-gate goto out; 3330Sstevel@tonic-gate } 3340Sstevel@tonic-gate if (scf_transaction_start(tx, pg) != SCF_SUCCESS) { 3350Sstevel@tonic-gate if (scf_error() != SCF_ERROR_PERMISSION_DENIED) { 3360Sstevel@tonic-gate if (errstr != NULL) 3370Sstevel@tonic-gate *errstr = gettext("Could not start " 3380Sstevel@tonic-gate "hash transaction.\n"); 3390Sstevel@tonic-gate result = -1; 3400Sstevel@tonic-gate goto out; 3410Sstevel@tonic-gate } 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate if (errstr != NULL) 3440Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 3450Sstevel@tonic-gate "permission denied.\n"); 3460Sstevel@tonic-gate result = -1; 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate scf_transaction_destroy(tx); 3490Sstevel@tonic-gate (void) scf_entry_destroy(e); 3500Sstevel@tonic-gate goto out; 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate if (scf_transaction_property_new(tx, e, MHASH_PROP, 3540Sstevel@tonic-gate SCF_TYPE_OPAQUE) != SCF_SUCCESS && 3550Sstevel@tonic-gate scf_transaction_property_change_type(tx, e, MHASH_PROP, 3560Sstevel@tonic-gate SCF_TYPE_OPAQUE) != SCF_SUCCESS) { 3570Sstevel@tonic-gate if (errstr != NULL) 3580Sstevel@tonic-gate *errstr = gettext("Could not modify hash " 3590Sstevel@tonic-gate "entry"); 3600Sstevel@tonic-gate result = -1; 3610Sstevel@tonic-gate goto out; 3620Sstevel@tonic-gate } 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate ret = scf_entry_add_value(e, val); 3650Sstevel@tonic-gate assert(ret == SCF_SUCCESS); 3660Sstevel@tonic-gate 3670Sstevel@tonic-gate ret = scf_transaction_commit(tx); 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate if (ret == 0) 3700Sstevel@tonic-gate scf_transaction_reset(tx); 3710Sstevel@tonic-gate } while (ret == 0); 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate if (ret < 0) { 3740Sstevel@tonic-gate if (scf_error() != SCF_ERROR_PERMISSION_DENIED) { 3750Sstevel@tonic-gate if (errstr != NULL) 3760Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 3770Sstevel@tonic-gate "permission denied.\n"); 3780Sstevel@tonic-gate result = -1; 3790Sstevel@tonic-gate goto out; 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate if (errstr != NULL) 3830Sstevel@tonic-gate *errstr = gettext("Could not commit transaction"); 3840Sstevel@tonic-gate result = -1; 3850Sstevel@tonic-gate } 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate scf_transaction_destroy(tx); 3880Sstevel@tonic-gate (void) scf_entry_destroy(e); 3890Sstevel@tonic-gate 3900Sstevel@tonic-gate out: 3910Sstevel@tonic-gate (void) scf_value_destroy(val); 3920Sstevel@tonic-gate scf_property_destroy(prop); 3930Sstevel@tonic-gate scf_pg_destroy(pg); 3940Sstevel@tonic-gate scf_service_destroy(svc); 3950Sstevel@tonic-gate scf_scope_destroy(scope); 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate return (result); 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate /* 4011061Scasper * Generate the md5 hash of a file; manifest files are smallish 4021061Scasper * so we can read them in one gulp. 4031061Scasper */ 4041061Scasper static int 4051061Scasper md5_hash_file(const char *file, off64_t sz, uchar_t *hash) 4061061Scasper { 4071061Scasper char *buf; 4081061Scasper int fd; 4091061Scasper ssize_t res; 4101061Scasper int ret; 4111061Scasper 4121061Scasper fd = open(file, O_RDONLY); 4131061Scasper if (fd < 0) 4141061Scasper return (-1); 4151061Scasper 4161061Scasper buf = malloc(sz); 4171061Scasper if (buf == NULL) { 4181061Scasper (void) close(fd); 4191061Scasper return (-1); 4201061Scasper } 4211061Scasper 4221061Scasper res = read(fd, buf, (size_t)sz); 4231061Scasper 4241061Scasper (void) close(fd); 4251061Scasper 4261061Scasper if (res == sz) { 4271061Scasper ret = 0; 4281061Scasper md5_calc(hash, (uchar_t *)buf, (unsigned int) sz); 4291061Scasper } else { 4301061Scasper ret = -1; 4311061Scasper } 4321061Scasper 4331061Scasper free(buf); 4341061Scasper return (ret); 4351061Scasper } 4361061Scasper 4371061Scasper /* 4380Sstevel@tonic-gate * int mhash_test_file(scf_handle_t *, const char *, uint_t, char **, uchar_t *) 4390Sstevel@tonic-gate * Test the given filename against the hashed metadata in the repository. 4400Sstevel@tonic-gate * The behaviours for import and apply are slightly different. For imports, 4410Sstevel@tonic-gate * if the hash value is absent or different, then the import operation 4420Sstevel@tonic-gate * continues. For profile application, the operation continues only if the 4430Sstevel@tonic-gate * hash value for the file is absent. 4440Sstevel@tonic-gate * 445*2462Scasper * We keep two hashes: one which can be quickly test: the metadata hash, 446*2462Scasper * and one which is more expensive to test: the file contents hash. 447*2462Scasper * 448*2462Scasper * If either hash matches, the file does not need to be re-read. 449*2462Scasper * If only one of the hashes matches, a side effect of this function 450*2462Scasper * is to store the newly computed hash. 451*2462Scasper * If neither hash matches, the hash computed for the new file is returned 452*2462Scasper * and not stored. 453*2462Scasper * 454*2462Scasper * Return values: 455*2462Scasper * MHASH_NEWFILE - the file no longer matches the hash or no hash existed 456*2462Scasper * ONLY in this case we return the new file's hash. 457*2462Scasper * MHASH_FAILURE - an internal error occurred, or the file was not found. 458*2462Scasper * MHASH_RECONCILED- based on the metadata/file hash, the file does 459*2462Scasper * not need to be re-read; if necessary, 460*2462Scasper * the hash was upgraded or reconciled. 461*2462Scasper * 462*2462Scasper * NOTE: no hash is returned UNLESS MHASH_NEWFILE is returned. 4630Sstevel@tonic-gate */ 4640Sstevel@tonic-gate int 4650Sstevel@tonic-gate mhash_test_file(scf_handle_t *hndl, const char *file, uint_t is_profile, 466*2462Scasper char **pnamep, uchar_t *hashbuf) 4670Sstevel@tonic-gate { 4680Sstevel@tonic-gate boolean_t do_hash; 4690Sstevel@tonic-gate struct stat64 st; 4700Sstevel@tonic-gate char *cp; 4710Sstevel@tonic-gate char *data; 4720Sstevel@tonic-gate uchar_t stored_hash[MHASH_SIZE]; 473*2462Scasper uchar_t hash[MHASH_SIZE]; 4740Sstevel@tonic-gate char *pname; 4750Sstevel@tonic-gate int ret; 476*2462Scasper int hashash; 477*2462Scasper int metahashok = 0; 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* 4800Sstevel@tonic-gate * In the case where we are doing automated imports, we reduce the UID, 4810Sstevel@tonic-gate * the GID, the size, and the mtime into a string (to eliminate 4820Sstevel@tonic-gate * endianness) which we then make opaque as a single MD5 digest. 4830Sstevel@tonic-gate * 4840Sstevel@tonic-gate * The previous hash was composed of the inode number, the UID, the file 4850Sstevel@tonic-gate * size, and the mtime. This formulation was found to be insufficiently 4860Sstevel@tonic-gate * portable for use in highly replicated deployments. The current 4870Sstevel@tonic-gate * algorithm will allow matches of this "v1" hash, but always returns 4880Sstevel@tonic-gate * the effective "v2" hash, such that updates result in the more 4890Sstevel@tonic-gate * portable hash being used. 4900Sstevel@tonic-gate * 4911061Scasper * An unwanted side effect of a hash based solely on the file 4921061Scasper * meta data is the fact that we pay no attention to the contents 4931061Scasper * which may remain the same despite meta data changes. This happens 4941061Scasper * with (live) upgrades. We extend the V2 hash with an additional 4951061Scasper * digest of the file contents and the code retrieving the hash 4961061Scasper * from the repository zero fills the remainder so we can detect 4971061Scasper * it is missing. 4981061Scasper * 4991061Scasper * If the the V2 digest matches, we check for the presence of 5001061Scasper * the contents digest and compute and store it if missing. 5011061Scasper * 5021061Scasper * If the V2 digest doesn't match but we also have a non-zero 5031061Scasper * file hash, we match the file content digest. If it matches, 5041061Scasper * we compute and store the new complete hash so that later 5051061Scasper * checks will find the meta data digest correct. 5061061Scasper * 5071061Scasper * If the above matches fail and the V1 hash doesn't match either, 5081061Scasper * we consider the test to have failed, implying that some aspect 5091061Scasper * of the manifest has changed. 5100Sstevel@tonic-gate */ 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate cp = getenv("SVCCFG_CHECKHASH"); 5130Sstevel@tonic-gate do_hash = (cp != NULL && *cp != '\0'); 5140Sstevel@tonic-gate if (!do_hash) { 515*2462Scasper if (pnamep != NULL) 516*2462Scasper *pnamep = NULL; 517*2462Scasper return (MHASH_NEWFILE); 518*2462Scasper } 519*2462Scasper 520*2462Scasper pname = mhash_filename_to_propname(file); 521*2462Scasper if (pname == NULL) 522*2462Scasper return (MHASH_FAILURE); 523*2462Scasper 524*2462Scasper hashash = mhash_retrieve_entry(hndl, pname, stored_hash) == 0; 525*2462Scasper 526*2462Scasper /* Never reread a profile. */ 527*2462Scasper if (hashash && is_profile) { 528*2462Scasper uu_free(pname); 529*2462Scasper return (MHASH_RECONCILED); 530*2462Scasper } 531*2462Scasper 532*2462Scasper /* 533*2462Scasper * No hash and not interested in one, then don't bother computing it. 534*2462Scasper * We also skip returning the property name in that case. 535*2462Scasper */ 536*2462Scasper if (!hashash && hashbuf == NULL) { 537*2462Scasper uu_free(pname); 538*2462Scasper return (MHASH_NEWFILE); 5390Sstevel@tonic-gate } 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate do 5420Sstevel@tonic-gate ret = stat64(file, &st); 5430Sstevel@tonic-gate while (ret < 0 && errno == EINTR); 5440Sstevel@tonic-gate if (ret < 0) { 545*2462Scasper uu_free(pname); 546*2462Scasper return (MHASH_FAILURE); 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate data = uu_msprintf(MHASH_FORMAT_V2, st.st_uid, st.st_gid, 5500Sstevel@tonic-gate st.st_size, st.st_mtime); 5510Sstevel@tonic-gate if (data == NULL) { 552*2462Scasper uu_free(pname); 553*2462Scasper return (MHASH_FAILURE); 5540Sstevel@tonic-gate } 5550Sstevel@tonic-gate 5561061Scasper (void) memset(hash, 0, MHASH_SIZE); 5570Sstevel@tonic-gate md5_calc(hash, (uchar_t *)data, strlen(data)); 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate uu_free(data); 5600Sstevel@tonic-gate 561*2462Scasper /* 562*2462Scasper * Verify the meta data hash. 563*2462Scasper */ 564*2462Scasper if (hashash && memcmp(hash, stored_hash, MD5_DIGEST_LENGTH) == 0) { 565*2462Scasper int i; 566*2462Scasper 567*2462Scasper metahashok = 1; 568*2462Scasper /* 569*2462Scasper * The metadata hash matches; now we see if there was a 570*2462Scasper * content hash; if not, we will continue on and compute and 571*2462Scasper * store the updated hash. 572*2462Scasper * If there was no content hash, mhash_retrieve_entry() 573*2462Scasper * will have zero filled it. 574*2462Scasper */ 575*2462Scasper for (i = 0; i < MD5_DIGEST_LENGTH; i++) { 576*2462Scasper if (stored_hash[MD5_DIGEST_LENGTH+i] != 0) { 577*2462Scasper uu_free(pname); 578*2462Scasper return (MHASH_RECONCILED); 579*2462Scasper } 580*2462Scasper } 581*2462Scasper } 5820Sstevel@tonic-gate 583*2462Scasper /* 584*2462Scasper * Compute the file hash as we can no longer avoid having to know it. 585*2462Scasper * Note: from this point on "hash" contains the full, current, hash. 586*2462Scasper */ 587*2462Scasper if (md5_hash_file(file, st.st_size, &hash[MHASH_SIZE_OLD]) != 0) { 588*2462Scasper uu_free(pname); 589*2462Scasper return (MHASH_FAILURE); 590*2462Scasper } 591*2462Scasper if (hashash) { 592*2462Scasper uchar_t hash_v1[MHASH_SIZE_OLD]; 5930Sstevel@tonic-gate 594*2462Scasper if (metahashok || 595*2462Scasper memcmp(&hash[MHASH_SIZE_OLD], &stored_hash[MHASH_SIZE_OLD], 596*2462Scasper MD5_DIGEST_LENGTH) == 0) { 597*2462Scasper 598*2462Scasper /* 599*2462Scasper * Reconcile entry: we get here when either the 600*2462Scasper * meta data hash matches or the content hash matches; 601*2462Scasper * we then update the database with the complete 602*2462Scasper * new hash so we can be a bit quicker next time. 603*2462Scasper */ 604*2462Scasper (void) mhash_store_entry(hndl, pname, hash, NULL); 6051061Scasper uu_free(pname); 606*2462Scasper return (MHASH_RECONCILED); 6070Sstevel@tonic-gate } 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate /* 610*2462Scasper * No match on V2 hash or file content; compare V1 hash. 6110Sstevel@tonic-gate */ 6120Sstevel@tonic-gate data = uu_msprintf(MHASH_FORMAT_V1, st.st_ino, st.st_uid, 6130Sstevel@tonic-gate st.st_size, st.st_mtime); 6141061Scasper if (data == NULL) { 6151061Scasper uu_free(pname); 616*2462Scasper return (MHASH_FAILURE); 6171061Scasper } 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate md5_calc(hash_v1, (uchar_t *)data, strlen(data)); 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate uu_free(data); 6220Sstevel@tonic-gate 6231061Scasper if (memcmp(hash_v1, stored_hash, MD5_DIGEST_LENGTH) == 0) { 624*2462Scasper /* 625*2462Scasper * Update the new entry so we don't have to go through 626*2462Scasper * all this trouble next time. 627*2462Scasper */ 628*2462Scasper (void) mhash_store_entry(hndl, pname, hash, NULL); 6291061Scasper uu_free(pname); 630*2462Scasper return (MHASH_RECONCILED); 6311061Scasper } 6320Sstevel@tonic-gate } 6330Sstevel@tonic-gate 634*2462Scasper if (pnamep != NULL) 635*2462Scasper *pnamep = pname; 636*2462Scasper else 637*2462Scasper uu_free(pname); 6380Sstevel@tonic-gate 639*2462Scasper if (hashbuf != NULL) 640*2462Scasper (void) memcpy(hashbuf, hash, MHASH_SIZE); 641*2462Scasper 642*2462Scasper return (MHASH_NEWFILE); 6430Sstevel@tonic-gate } 644