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 5*1061Scasper * Common Development and Distribution License (the "License"). 6*1061Scasper * 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 /* 220Sstevel@tonic-gate * Copyright 2005 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> 34*1061Scasper #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> 45*1061Scasper #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 160*1061Scasper /* 161*1061Scasper * Make sure that the old hash is returned with 162*1061Scasper * remainder of the bytes zeroed. 163*1061Scasper */ 164*1061Scasper if (szret == MHASH_SIZE_OLD) { 165*1061Scasper (void) memset(hash + MHASH_SIZE_OLD, 0, 166*1061Scasper MHASH_SIZE - MHASH_SIZE_OLD); 167*1061Scasper } 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 /* 401*1061Scasper * Generate the md5 hash of a file; manifest files are smallish 402*1061Scasper * so we can read them in one gulp. 403*1061Scasper */ 404*1061Scasper static int 405*1061Scasper md5_hash_file(const char *file, off64_t sz, uchar_t *hash) 406*1061Scasper { 407*1061Scasper char *buf; 408*1061Scasper int fd; 409*1061Scasper ssize_t res; 410*1061Scasper int ret; 411*1061Scasper 412*1061Scasper fd = open(file, O_RDONLY); 413*1061Scasper if (fd < 0) 414*1061Scasper return (-1); 415*1061Scasper 416*1061Scasper buf = malloc(sz); 417*1061Scasper if (buf == NULL) { 418*1061Scasper (void) close(fd); 419*1061Scasper return (-1); 420*1061Scasper } 421*1061Scasper 422*1061Scasper res = read(fd, buf, (size_t)sz); 423*1061Scasper 424*1061Scasper (void) close(fd); 425*1061Scasper 426*1061Scasper if (res == sz) { 427*1061Scasper ret = 0; 428*1061Scasper md5_calc(hash, (uchar_t *)buf, (unsigned int) sz); 429*1061Scasper } else { 430*1061Scasper ret = -1; 431*1061Scasper } 432*1061Scasper 433*1061Scasper free(buf); 434*1061Scasper return (ret); 435*1061Scasper } 436*1061Scasper 437*1061Scasper /* 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 * 4450Sstevel@tonic-gate * Return non-zero if we should skip the file because it is unchanged or 4460Sstevel@tonic-gate * nonexistent. 4470Sstevel@tonic-gate */ 4480Sstevel@tonic-gate int 4490Sstevel@tonic-gate mhash_test_file(scf_handle_t *hndl, const char *file, uint_t is_profile, 4500Sstevel@tonic-gate char **pnamep, uchar_t *hash) 4510Sstevel@tonic-gate { 4520Sstevel@tonic-gate boolean_t do_hash; 4530Sstevel@tonic-gate struct stat64 st; 4540Sstevel@tonic-gate char *cp; 4550Sstevel@tonic-gate char *data; 4560Sstevel@tonic-gate uchar_t stored_hash[MHASH_SIZE]; 4570Sstevel@tonic-gate char *pname; 4580Sstevel@tonic-gate int ret; 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate /* 4610Sstevel@tonic-gate * In the case where we are doing automated imports, we reduce the UID, 4620Sstevel@tonic-gate * the GID, the size, and the mtime into a string (to eliminate 4630Sstevel@tonic-gate * endianness) which we then make opaque as a single MD5 digest. 4640Sstevel@tonic-gate * 4650Sstevel@tonic-gate * The previous hash was composed of the inode number, the UID, the file 4660Sstevel@tonic-gate * size, and the mtime. This formulation was found to be insufficiently 4670Sstevel@tonic-gate * portable for use in highly replicated deployments. The current 4680Sstevel@tonic-gate * algorithm will allow matches of this "v1" hash, but always returns 4690Sstevel@tonic-gate * the effective "v2" hash, such that updates result in the more 4700Sstevel@tonic-gate * portable hash being used. 4710Sstevel@tonic-gate * 472*1061Scasper * An unwanted side effect of a hash based solely on the file 473*1061Scasper * meta data is the fact that we pay no attention to the contents 474*1061Scasper * which may remain the same despite meta data changes. This happens 475*1061Scasper * with (live) upgrades. We extend the V2 hash with an additional 476*1061Scasper * digest of the file contents and the code retrieving the hash 477*1061Scasper * from the repository zero fills the remainder so we can detect 478*1061Scasper * it is missing. 479*1061Scasper * 480*1061Scasper * If the the V2 digest matches, we check for the presence of 481*1061Scasper * the contents digest and compute and store it if missing. 482*1061Scasper * 483*1061Scasper * If the V2 digest doesn't match but we also have a non-zero 484*1061Scasper * file hash, we match the file content digest. If it matches, 485*1061Scasper * we compute and store the new complete hash so that later 486*1061Scasper * checks will find the meta data digest correct. 487*1061Scasper * 488*1061Scasper * If the above matches fail and the V1 hash doesn't match either, 489*1061Scasper * we consider the test to have failed, implying that some aspect 490*1061Scasper * of the manifest has changed. 4910Sstevel@tonic-gate */ 4920Sstevel@tonic-gate 4930Sstevel@tonic-gate cp = getenv("SVCCFG_CHECKHASH"); 4940Sstevel@tonic-gate do_hash = (cp != NULL && *cp != '\0'); 4950Sstevel@tonic-gate if (!do_hash) { 4960Sstevel@tonic-gate *pnamep = NULL; 4970Sstevel@tonic-gate return (0); 4980Sstevel@tonic-gate } 4990Sstevel@tonic-gate 5000Sstevel@tonic-gate do 5010Sstevel@tonic-gate ret = stat64(file, &st); 5020Sstevel@tonic-gate while (ret < 0 && errno == EINTR); 5030Sstevel@tonic-gate if (ret < 0) { 5040Sstevel@tonic-gate return (-1); 5050Sstevel@tonic-gate } 5060Sstevel@tonic-gate 5070Sstevel@tonic-gate data = uu_msprintf(MHASH_FORMAT_V2, st.st_uid, st.st_gid, 5080Sstevel@tonic-gate st.st_size, st.st_mtime); 5090Sstevel@tonic-gate if (data == NULL) { 5100Sstevel@tonic-gate return (-1); 5110Sstevel@tonic-gate } 5120Sstevel@tonic-gate 513*1061Scasper (void) memset(hash, 0, MHASH_SIZE); 5140Sstevel@tonic-gate md5_calc(hash, (uchar_t *)data, strlen(data)); 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate uu_free(data); 5170Sstevel@tonic-gate 5180Sstevel@tonic-gate pname = mhash_filename_to_propname(file); 5190Sstevel@tonic-gate if (pname == NULL) 5200Sstevel@tonic-gate return (-1); 5210Sstevel@tonic-gate 5220Sstevel@tonic-gate if (mhash_retrieve_entry(hndl, pname, stored_hash) == 0) { 5230Sstevel@tonic-gate uchar_t hash_v1[MHASH_SIZE]; 5240Sstevel@tonic-gate 5250Sstevel@tonic-gate if (is_profile) { 526*1061Scasper uu_free(pname); 5270Sstevel@tonic-gate return (1); 5280Sstevel@tonic-gate } 5290Sstevel@tonic-gate 5300Sstevel@tonic-gate /* 5310Sstevel@tonic-gate * Manifest import. 5320Sstevel@tonic-gate */ 533*1061Scasper if (memcmp(hash, stored_hash, MD5_DIGEST_LENGTH) == 0) { 534*1061Scasper int i; 535*1061Scasper 536*1061Scasper /* 537*1061Scasper * If there's no recorded file hash, record it. 538*1061Scasper */ 539*1061Scasper for (i = 0; i < MD5_DIGEST_LENGTH; i++) { 540*1061Scasper if (stored_hash[MD5_DIGEST_LENGTH+i] != 0) 541*1061Scasper break; 542*1061Scasper } 543*1061Scasper if (i == MD5_DIGEST_LENGTH) { 544*1061Scasper if (md5_hash_file(file, st.st_size, 545*1061Scasper &hash[MHASH_SIZE_OLD]) == 0) { 546*1061Scasper (void) mhash_store_entry(hndl, pname, hash, 547*1061Scasper NULL); 548*1061Scasper } 549*1061Scasper } else { 550*1061Scasper /* Always return hash with MD5 content hash */ 551*1061Scasper (void) memcpy(hash, stored_hash, MHASH_SIZE); 552*1061Scasper } 553*1061Scasper uu_free(pname); 5540Sstevel@tonic-gate return (1); 555*1061Scasper } 556*1061Scasper 557*1061Scasper /* 558*1061Scasper * The remainder of our hash is all 0; if the returned hash 559*1061Scasper * is not, it's a V2 hash + file checksum; so we're going 560*1061Scasper * to hash the file and then compare the checksum again. 561*1061Scasper */ 562*1061Scasper if (memcmp(&hash[MHASH_SIZE_OLD], &stored_hash[MHASH_SIZE_OLD], 563*1061Scasper MD5_DIGEST_LENGTH) != 0 && 564*1061Scasper md5_hash_file(file, st.st_size, 565*1061Scasper &hash[MHASH_SIZE_OLD]) == 0 && 566*1061Scasper memcmp(&hash[MHASH_SIZE_OLD], &stored_hash[MHASH_SIZE_OLD], 567*1061Scasper MD5_DIGEST_LENGTH) == 0) { 568*1061Scasper 569*1061Scasper /* Be kind and update the entry */ 570*1061Scasper (void) mhash_store_entry(hndl, pname, hash, NULL); 571*1061Scasper uu_free(pname); 572*1061Scasper return (1); 573*1061Scasper } 5740Sstevel@tonic-gate 5750Sstevel@tonic-gate /* 5760Sstevel@tonic-gate * No match on V2 hash; compare V1 hash. 5770Sstevel@tonic-gate */ 5780Sstevel@tonic-gate data = uu_msprintf(MHASH_FORMAT_V1, st.st_ino, st.st_uid, 5790Sstevel@tonic-gate st.st_size, st.st_mtime); 580*1061Scasper if (data == NULL) { 581*1061Scasper uu_free(pname); 5820Sstevel@tonic-gate return (-1); 583*1061Scasper } 5840Sstevel@tonic-gate 5850Sstevel@tonic-gate md5_calc(hash_v1, (uchar_t *)data, strlen(data)); 5860Sstevel@tonic-gate 5870Sstevel@tonic-gate uu_free(data); 5880Sstevel@tonic-gate 589*1061Scasper if (memcmp(hash_v1, stored_hash, MD5_DIGEST_LENGTH) == 0) { 590*1061Scasper uu_free(pname); 5910Sstevel@tonic-gate return (1); 592*1061Scasper } 5930Sstevel@tonic-gate } 5940Sstevel@tonic-gate 5950Sstevel@tonic-gate *pnamep = pname; 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate return (0); 5980Sstevel@tonic-gate } 599