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*10461SSean.Wilcox@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate 270Sstevel@tonic-gate #include <sys/stat.h> 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate 300Sstevel@tonic-gate #include <assert.h> 310Sstevel@tonic-gate #include <ctype.h> 320Sstevel@tonic-gate #include <errno.h> 331061Scasper #include <fcntl.h> 340Sstevel@tonic-gate #include <libintl.h> 350Sstevel@tonic-gate #include <libscf.h> 360Sstevel@tonic-gate #include <libuutil.h> 370Sstevel@tonic-gate #include <limits.h> 380Sstevel@tonic-gate #include <md5.h> 390Sstevel@tonic-gate #include <pthread.h> 400Sstevel@tonic-gate #include <stdio.h> 410Sstevel@tonic-gate #include <stdlib.h> 420Sstevel@tonic-gate #include <string.h> 430Sstevel@tonic-gate #include <strings.h> 441061Scasper #include <unistd.h> 450Sstevel@tonic-gate 460Sstevel@tonic-gate #include <manifest_hash.h> 470Sstevel@tonic-gate 480Sstevel@tonic-gate /* 490Sstevel@tonic-gate * Translate a file name to property name. Return an allocated string or NULL 507475SPhilippe.Jung@Sun.COM * if realpath() fails. If deathrow is true, realpath() is skipped. This 517475SPhilippe.Jung@Sun.COM * allows to return the property name even if the file doesn't exist. 520Sstevel@tonic-gate */ 530Sstevel@tonic-gate char * 547475SPhilippe.Jung@Sun.COM mhash_filename_to_propname(const char *in, boolean_t deathrow) 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); 607475SPhilippe.Jung@Sun.COM if (deathrow) { 617475SPhilippe.Jung@Sun.COM /* used only for service deathrow handling */ 627475SPhilippe.Jung@Sun.COM if (strlcpy(out, in, PATH_MAX + 1) >= (PATH_MAX + 1)) { 637475SPhilippe.Jung@Sun.COM uu_free(out); 647475SPhilippe.Jung@Sun.COM return (NULL); 657475SPhilippe.Jung@Sun.COM } 667475SPhilippe.Jung@Sun.COM } else { 677475SPhilippe.Jung@Sun.COM if (realpath(in, out) == NULL) { 687475SPhilippe.Jung@Sun.COM uu_free(out); 697475SPhilippe.Jung@Sun.COM return (NULL); 707475SPhilippe.Jung@Sun.COM } 710Sstevel@tonic-gate } 720Sstevel@tonic-gate 730Sstevel@tonic-gate base = getenv("PKG_INSTALL_ROOT"); 740Sstevel@tonic-gate 750Sstevel@tonic-gate /* 760Sstevel@tonic-gate * We copy-shift over the basedir and the leading slash, since it's 770Sstevel@tonic-gate * not relevant to when we boot with this repository. 780Sstevel@tonic-gate */ 790Sstevel@tonic-gate 800Sstevel@tonic-gate cp = out + ((base != NULL)? strlen(base) : 0); 810Sstevel@tonic-gate if (*cp == '/') 820Sstevel@tonic-gate cp++; 830Sstevel@tonic-gate (void) memmove(out, cp, strlen(cp) + 1); 840Sstevel@tonic-gate 850Sstevel@tonic-gate len = strlen(out); 860Sstevel@tonic-gate if (len > scf_limit(SCF_LIMIT_MAX_NAME_LENGTH)) { 870Sstevel@tonic-gate /* Use the first half and the second half. */ 880Sstevel@tonic-gate piece_len = (scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) - 3) / 2; 890Sstevel@tonic-gate 900Sstevel@tonic-gate (void) strncpy(out + piece_len, "__", 2); 910Sstevel@tonic-gate 920Sstevel@tonic-gate (void) memmove(out + piece_len + 2, out + (len - piece_len), 930Sstevel@tonic-gate piece_len + 1); 940Sstevel@tonic-gate } 950Sstevel@tonic-gate 960Sstevel@tonic-gate /* 970Sstevel@tonic-gate * Translate non-property characters to '_', first making sure that 980Sstevel@tonic-gate * we don't begin with '_'. 990Sstevel@tonic-gate */ 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate if (!isalpha(*out)) 1020Sstevel@tonic-gate *out = 'A'; 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate for (cp = out + 1; *cp != '\0'; ++cp) { 1050Sstevel@tonic-gate if (!(isalnum(*cp) || *cp == '_' || *cp == '-')) 1060Sstevel@tonic-gate *cp = '_'; 1070Sstevel@tonic-gate } 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate return (out); 1100Sstevel@tonic-gate } 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate int 1130Sstevel@tonic-gate mhash_retrieve_entry(scf_handle_t *hndl, const char *name, uchar_t *hash) 1140Sstevel@tonic-gate { 1150Sstevel@tonic-gate scf_scope_t *scope; 1160Sstevel@tonic-gate scf_service_t *svc; 1170Sstevel@tonic-gate scf_propertygroup_t *pg; 1180Sstevel@tonic-gate scf_property_t *prop; 1190Sstevel@tonic-gate scf_value_t *val; 1200Sstevel@tonic-gate ssize_t szret; 1210Sstevel@tonic-gate int result = 0; 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate /* 1240Sstevel@tonic-gate * In this implementation the hash for name is the opaque value of 1250Sstevel@tonic-gate * svc:/MHASH_SVC/:properties/name/MHASH_PROP 1260Sstevel@tonic-gate */ 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate if ((scope = scf_scope_create(hndl)) == NULL || 1290Sstevel@tonic-gate (svc = scf_service_create(hndl)) == NULL || 1300Sstevel@tonic-gate (pg = scf_pg_create(hndl)) == NULL || 1310Sstevel@tonic-gate (prop = scf_property_create(hndl)) == NULL || 1320Sstevel@tonic-gate (val = scf_value_create(hndl)) == NULL) { 1330Sstevel@tonic-gate result = -1; 1340Sstevel@tonic-gate goto out; 1350Sstevel@tonic-gate } 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate if (scf_handle_get_local_scope(hndl, scope) < 0) { 1380Sstevel@tonic-gate result = -1; 1390Sstevel@tonic-gate goto out; 1400Sstevel@tonic-gate } 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate if (scf_scope_get_service(scope, MHASH_SVC, svc) < 0) { 1430Sstevel@tonic-gate result = -1; 1440Sstevel@tonic-gate goto out; 1450Sstevel@tonic-gate } 1460Sstevel@tonic-gate 1470Sstevel@tonic-gate if (scf_service_get_pg(svc, name, pg) != SCF_SUCCESS) { 1480Sstevel@tonic-gate result = -1; 1490Sstevel@tonic-gate goto out; 1500Sstevel@tonic-gate } 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate if (scf_pg_get_property(pg, MHASH_PROP, prop) != SCF_SUCCESS) { 1530Sstevel@tonic-gate result = -1; 1540Sstevel@tonic-gate goto out; 1550Sstevel@tonic-gate } 1560Sstevel@tonic-gate 1570Sstevel@tonic-gate if (scf_property_get_value(prop, val) != SCF_SUCCESS) { 1580Sstevel@tonic-gate result = -1; 1590Sstevel@tonic-gate goto out; 1600Sstevel@tonic-gate } 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate szret = scf_value_get_opaque(val, hash, MHASH_SIZE); 1630Sstevel@tonic-gate if (szret < 0) { 1640Sstevel@tonic-gate result = -1; 1650Sstevel@tonic-gate goto out; 1660Sstevel@tonic-gate } 1670Sstevel@tonic-gate 1681061Scasper /* 1691061Scasper * Make sure that the old hash is returned with 1701061Scasper * remainder of the bytes zeroed. 1711061Scasper */ 1721061Scasper if (szret == MHASH_SIZE_OLD) { 1731061Scasper (void) memset(hash + MHASH_SIZE_OLD, 0, 1741061Scasper MHASH_SIZE - MHASH_SIZE_OLD); 1751061Scasper } else if (szret != MHASH_SIZE) { 1760Sstevel@tonic-gate scf_value_destroy(val); 1770Sstevel@tonic-gate result = -1; 1780Sstevel@tonic-gate goto out; 1790Sstevel@tonic-gate } 1800Sstevel@tonic-gate 1810Sstevel@tonic-gate out: 1820Sstevel@tonic-gate (void) scf_value_destroy(val); 1830Sstevel@tonic-gate scf_property_destroy(prop); 1840Sstevel@tonic-gate scf_pg_destroy(pg); 1850Sstevel@tonic-gate scf_service_destroy(svc); 1860Sstevel@tonic-gate scf_scope_destroy(scope); 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate return (result); 1890Sstevel@tonic-gate } 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate int 192*10461SSean.Wilcox@Sun.COM mhash_store_entry(scf_handle_t *hndl, const char *name, const char *fname, 193*10461SSean.Wilcox@Sun.COM uchar_t *hash, char **errstr) 1940Sstevel@tonic-gate { 1950Sstevel@tonic-gate scf_scope_t *scope = NULL; 1960Sstevel@tonic-gate scf_service_t *svc = NULL; 1970Sstevel@tonic-gate scf_propertygroup_t *pg = NULL; 1980Sstevel@tonic-gate scf_property_t *prop = NULL; 1990Sstevel@tonic-gate scf_value_t *val = NULL; 200*10461SSean.Wilcox@Sun.COM scf_value_t *fval = NULL; 2010Sstevel@tonic-gate scf_transaction_t *tx = NULL; 2020Sstevel@tonic-gate scf_transaction_entry_t *e = NULL; 203*10461SSean.Wilcox@Sun.COM scf_transaction_entry_t *fe = NULL; 2040Sstevel@tonic-gate int ret, result = 0; 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate int i; 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate if ((scope = scf_scope_create(hndl)) == NULL || 2090Sstevel@tonic-gate (svc = scf_service_create(hndl)) == NULL || 2100Sstevel@tonic-gate (pg = scf_pg_create(hndl)) == NULL || 2110Sstevel@tonic-gate (prop = scf_property_create(hndl)) == NULL) { 2120Sstevel@tonic-gate if (errstr != NULL) 2130Sstevel@tonic-gate *errstr = gettext("Could not create scf objects"); 2140Sstevel@tonic-gate result = -1; 2150Sstevel@tonic-gate goto out; 2160Sstevel@tonic-gate } 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate if (scf_handle_get_local_scope(hndl, scope) != SCF_SUCCESS) { 2190Sstevel@tonic-gate if (errstr != NULL) 2200Sstevel@tonic-gate *errstr = gettext("Could not get local scope"); 2210Sstevel@tonic-gate result = -1; 2220Sstevel@tonic-gate goto out; 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate for (i = 0; i < 5; ++i) { 2260Sstevel@tonic-gate scf_error_t err; 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate if (scf_scope_get_service(scope, MHASH_SVC, svc) == 2290Sstevel@tonic-gate SCF_SUCCESS) 2300Sstevel@tonic-gate break; 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate if (scf_error() != SCF_ERROR_NOT_FOUND) { 2330Sstevel@tonic-gate if (errstr != NULL) 2340Sstevel@tonic-gate *errstr = gettext("Could not get manifest hash " 2350Sstevel@tonic-gate "service"); 2360Sstevel@tonic-gate result = -1; 2370Sstevel@tonic-gate goto out; 2380Sstevel@tonic-gate } 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate if (scf_scope_add_service(scope, MHASH_SVC, svc) == 2410Sstevel@tonic-gate SCF_SUCCESS) 2420Sstevel@tonic-gate break; 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate err = scf_error(); 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate if (err == SCF_ERROR_EXISTS) 2470Sstevel@tonic-gate /* Try again. */ 2480Sstevel@tonic-gate continue; 2490Sstevel@tonic-gate else if (err == SCF_ERROR_PERMISSION_DENIED) { 2500Sstevel@tonic-gate if (errstr != NULL) 2510Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 2520Sstevel@tonic-gate "permission denied.\n"); 2530Sstevel@tonic-gate result = -1; 2540Sstevel@tonic-gate goto out; 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate if (errstr != NULL) 2580Sstevel@tonic-gate *errstr = gettext("Could not add manifest hash " 2590Sstevel@tonic-gate "service"); 2600Sstevel@tonic-gate result = -1; 2610Sstevel@tonic-gate goto out; 2620Sstevel@tonic-gate } 2630Sstevel@tonic-gate 2640Sstevel@tonic-gate if (i == 5) { 2650Sstevel@tonic-gate if (errstr != NULL) 2660Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 2670Sstevel@tonic-gate "service addition contention.\n"); 2680Sstevel@tonic-gate result = -1; 2690Sstevel@tonic-gate goto out; 2700Sstevel@tonic-gate } 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate for (i = 0; i < 5; ++i) { 2730Sstevel@tonic-gate scf_error_t err; 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate if (scf_service_get_pg(svc, name, pg) == SCF_SUCCESS) 2760Sstevel@tonic-gate break; 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate if (scf_error() != SCF_ERROR_NOT_FOUND) { 2790Sstevel@tonic-gate if (errstr != NULL) 2800Sstevel@tonic-gate *errstr = gettext("Could not get service's " 2810Sstevel@tonic-gate "hash record)"); 2820Sstevel@tonic-gate result = -1; 2830Sstevel@tonic-gate goto out; 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate if (scf_service_add_pg(svc, name, MHASH_PG_TYPE, 2870Sstevel@tonic-gate MHASH_PG_FLAGS, pg) == SCF_SUCCESS) 2880Sstevel@tonic-gate break; 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate err = scf_error(); 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate if (err == SCF_ERROR_EXISTS) 2930Sstevel@tonic-gate /* Try again. */ 2940Sstevel@tonic-gate continue; 2950Sstevel@tonic-gate else if (err == SCF_ERROR_PERMISSION_DENIED) { 2960Sstevel@tonic-gate if (errstr != NULL) 2970Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 2980Sstevel@tonic-gate "permission denied.\n"); 2990Sstevel@tonic-gate result = -1; 3000Sstevel@tonic-gate goto out; 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate if (errstr != NULL) 3040Sstevel@tonic-gate *errstr = gettext("Could not store file hash"); 3050Sstevel@tonic-gate result = -1; 3060Sstevel@tonic-gate goto out; 3070Sstevel@tonic-gate } 3080Sstevel@tonic-gate if (i == 5) { 3090Sstevel@tonic-gate if (errstr != NULL) 3100Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 3110Sstevel@tonic-gate "property group addition contention.\n"); 3120Sstevel@tonic-gate result = -1; 3130Sstevel@tonic-gate goto out; 3140Sstevel@tonic-gate } 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate if ((e = scf_entry_create(hndl)) == NULL || 317*10461SSean.Wilcox@Sun.COM (val = scf_value_create(hndl)) == NULL || 318*10461SSean.Wilcox@Sun.COM (fe = scf_entry_create(hndl)) == NULL || 319*10461SSean.Wilcox@Sun.COM (fval = scf_value_create(hndl)) == NULL) { 3200Sstevel@tonic-gate if (errstr != NULL) 3210Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 3220Sstevel@tonic-gate "permission denied.\n"); 3230Sstevel@tonic-gate result = -1; 3240Sstevel@tonic-gate goto out; 3250Sstevel@tonic-gate } 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate ret = scf_value_set_opaque(val, hash, MHASH_SIZE); 3280Sstevel@tonic-gate assert(ret == SCF_SUCCESS); 329*10461SSean.Wilcox@Sun.COM ret = scf_value_set_astring(fval, fname); 330*10461SSean.Wilcox@Sun.COM assert(ret == SCF_SUCCESS); 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate tx = scf_transaction_create(hndl); 3330Sstevel@tonic-gate if (tx == NULL) { 3340Sstevel@tonic-gate if (errstr != NULL) 3350Sstevel@tonic-gate *errstr = gettext("Could not create transaction"); 3360Sstevel@tonic-gate result = -1; 3370Sstevel@tonic-gate goto out; 3380Sstevel@tonic-gate } 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate do { 3410Sstevel@tonic-gate if (scf_pg_update(pg) == -1) { 3420Sstevel@tonic-gate if (errstr != NULL) 3430Sstevel@tonic-gate *errstr = gettext("Could not update hash " 3440Sstevel@tonic-gate "entry"); 3450Sstevel@tonic-gate result = -1; 3460Sstevel@tonic-gate goto out; 3470Sstevel@tonic-gate } 3480Sstevel@tonic-gate if (scf_transaction_start(tx, pg) != SCF_SUCCESS) { 3490Sstevel@tonic-gate if (scf_error() != SCF_ERROR_PERMISSION_DENIED) { 3500Sstevel@tonic-gate if (errstr != NULL) 3510Sstevel@tonic-gate *errstr = gettext("Could not start " 3520Sstevel@tonic-gate "hash transaction.\n"); 3530Sstevel@tonic-gate result = -1; 3540Sstevel@tonic-gate goto out; 3550Sstevel@tonic-gate } 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate if (errstr != NULL) 3580Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 3590Sstevel@tonic-gate "permission denied.\n"); 3600Sstevel@tonic-gate result = -1; 3610Sstevel@tonic-gate 3620Sstevel@tonic-gate scf_transaction_destroy(tx); 3630Sstevel@tonic-gate (void) scf_entry_destroy(e); 3640Sstevel@tonic-gate goto out; 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate 3670Sstevel@tonic-gate if (scf_transaction_property_new(tx, e, MHASH_PROP, 3680Sstevel@tonic-gate SCF_TYPE_OPAQUE) != SCF_SUCCESS && 3690Sstevel@tonic-gate scf_transaction_property_change_type(tx, e, MHASH_PROP, 3700Sstevel@tonic-gate SCF_TYPE_OPAQUE) != SCF_SUCCESS) { 3710Sstevel@tonic-gate if (errstr != NULL) 3720Sstevel@tonic-gate *errstr = gettext("Could not modify hash " 3730Sstevel@tonic-gate "entry"); 3740Sstevel@tonic-gate result = -1; 3750Sstevel@tonic-gate goto out; 3760Sstevel@tonic-gate } 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate ret = scf_entry_add_value(e, val); 3790Sstevel@tonic-gate assert(ret == SCF_SUCCESS); 3800Sstevel@tonic-gate 381*10461SSean.Wilcox@Sun.COM if (scf_transaction_property_new(tx, fe, MFILE_PROP, 382*10461SSean.Wilcox@Sun.COM SCF_TYPE_ASTRING) != SCF_SUCCESS && 383*10461SSean.Wilcox@Sun.COM scf_transaction_property_change_type(tx, fe, MFILE_PROP, 384*10461SSean.Wilcox@Sun.COM SCF_TYPE_ASTRING) != SCF_SUCCESS) { 385*10461SSean.Wilcox@Sun.COM if (errstr != NULL) 386*10461SSean.Wilcox@Sun.COM *errstr = gettext("Could not modify file " 387*10461SSean.Wilcox@Sun.COM "entry"); 388*10461SSean.Wilcox@Sun.COM result = -1; 389*10461SSean.Wilcox@Sun.COM goto out; 390*10461SSean.Wilcox@Sun.COM } 391*10461SSean.Wilcox@Sun.COM 392*10461SSean.Wilcox@Sun.COM ret = scf_entry_add_value(fe, fval); 393*10461SSean.Wilcox@Sun.COM assert(ret == SCF_SUCCESS); 394*10461SSean.Wilcox@Sun.COM 3950Sstevel@tonic-gate ret = scf_transaction_commit(tx); 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate if (ret == 0) 3980Sstevel@tonic-gate scf_transaction_reset(tx); 3990Sstevel@tonic-gate } while (ret == 0); 4000Sstevel@tonic-gate 4010Sstevel@tonic-gate if (ret < 0) { 4020Sstevel@tonic-gate if (scf_error() != SCF_ERROR_PERMISSION_DENIED) { 4030Sstevel@tonic-gate if (errstr != NULL) 4040Sstevel@tonic-gate *errstr = gettext("Could not store file hash: " 4050Sstevel@tonic-gate "permission denied.\n"); 4060Sstevel@tonic-gate result = -1; 4070Sstevel@tonic-gate goto out; 4080Sstevel@tonic-gate } 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate if (errstr != NULL) 4110Sstevel@tonic-gate *errstr = gettext("Could not commit transaction"); 4120Sstevel@tonic-gate result = -1; 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate scf_transaction_destroy(tx); 4160Sstevel@tonic-gate (void) scf_entry_destroy(e); 417*10461SSean.Wilcox@Sun.COM (void) scf_entry_destroy(fe); 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate out: 4200Sstevel@tonic-gate (void) scf_value_destroy(val); 421*10461SSean.Wilcox@Sun.COM (void) scf_value_destroy(fval); 4220Sstevel@tonic-gate scf_property_destroy(prop); 4230Sstevel@tonic-gate scf_pg_destroy(pg); 4240Sstevel@tonic-gate scf_service_destroy(svc); 4250Sstevel@tonic-gate scf_scope_destroy(scope); 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate return (result); 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate /* 4311061Scasper * Generate the md5 hash of a file; manifest files are smallish 4321061Scasper * so we can read them in one gulp. 4331061Scasper */ 4341061Scasper static int 4351061Scasper md5_hash_file(const char *file, off64_t sz, uchar_t *hash) 4361061Scasper { 4371061Scasper char *buf; 4381061Scasper int fd; 4391061Scasper ssize_t res; 4401061Scasper int ret; 4411061Scasper 4421061Scasper fd = open(file, O_RDONLY); 4431061Scasper if (fd < 0) 4441061Scasper return (-1); 4451061Scasper 4461061Scasper buf = malloc(sz); 4471061Scasper if (buf == NULL) { 4481061Scasper (void) close(fd); 4491061Scasper return (-1); 4501061Scasper } 4511061Scasper 4521061Scasper res = read(fd, buf, (size_t)sz); 4531061Scasper 4541061Scasper (void) close(fd); 4551061Scasper 4561061Scasper if (res == sz) { 4571061Scasper ret = 0; 4581061Scasper md5_calc(hash, (uchar_t *)buf, (unsigned int) sz); 4591061Scasper } else { 4601061Scasper ret = -1; 4611061Scasper } 4621061Scasper 4631061Scasper free(buf); 4641061Scasper return (ret); 4651061Scasper } 4661061Scasper 4671061Scasper /* 4680Sstevel@tonic-gate * int mhash_test_file(scf_handle_t *, const char *, uint_t, char **, uchar_t *) 4690Sstevel@tonic-gate * Test the given filename against the hashed metadata in the repository. 4700Sstevel@tonic-gate * The behaviours for import and apply are slightly different. For imports, 4710Sstevel@tonic-gate * if the hash value is absent or different, then the import operation 4720Sstevel@tonic-gate * continues. For profile application, the operation continues only if the 4730Sstevel@tonic-gate * hash value for the file is absent. 4740Sstevel@tonic-gate * 4752462Scasper * We keep two hashes: one which can be quickly test: the metadata hash, 4762462Scasper * and one which is more expensive to test: the file contents hash. 4772462Scasper * 4782462Scasper * If either hash matches, the file does not need to be re-read. 4792462Scasper * If only one of the hashes matches, a side effect of this function 4802462Scasper * is to store the newly computed hash. 4812462Scasper * If neither hash matches, the hash computed for the new file is returned 4822462Scasper * and not stored. 4832462Scasper * 4842462Scasper * Return values: 4852462Scasper * MHASH_NEWFILE - the file no longer matches the hash or no hash existed 4862462Scasper * ONLY in this case we return the new file's hash. 4872462Scasper * MHASH_FAILURE - an internal error occurred, or the file was not found. 4882462Scasper * MHASH_RECONCILED- based on the metadata/file hash, the file does 4892462Scasper * not need to be re-read; if necessary, 4902462Scasper * the hash was upgraded or reconciled. 4912462Scasper * 4922462Scasper * NOTE: no hash is returned UNLESS MHASH_NEWFILE is returned. 4930Sstevel@tonic-gate */ 4940Sstevel@tonic-gate int 4950Sstevel@tonic-gate mhash_test_file(scf_handle_t *hndl, const char *file, uint_t is_profile, 4962462Scasper char **pnamep, uchar_t *hashbuf) 4970Sstevel@tonic-gate { 4980Sstevel@tonic-gate boolean_t do_hash; 4990Sstevel@tonic-gate struct stat64 st; 5000Sstevel@tonic-gate char *cp; 5010Sstevel@tonic-gate char *data; 5020Sstevel@tonic-gate uchar_t stored_hash[MHASH_SIZE]; 5032462Scasper uchar_t hash[MHASH_SIZE]; 5040Sstevel@tonic-gate char *pname; 5050Sstevel@tonic-gate int ret; 5062462Scasper int hashash; 5072462Scasper int metahashok = 0; 5080Sstevel@tonic-gate 5090Sstevel@tonic-gate /* 5100Sstevel@tonic-gate * In the case where we are doing automated imports, we reduce the UID, 5110Sstevel@tonic-gate * the GID, the size, and the mtime into a string (to eliminate 5120Sstevel@tonic-gate * endianness) which we then make opaque as a single MD5 digest. 5130Sstevel@tonic-gate * 5140Sstevel@tonic-gate * The previous hash was composed of the inode number, the UID, the file 5150Sstevel@tonic-gate * size, and the mtime. This formulation was found to be insufficiently 5160Sstevel@tonic-gate * portable for use in highly replicated deployments. The current 5170Sstevel@tonic-gate * algorithm will allow matches of this "v1" hash, but always returns 5180Sstevel@tonic-gate * the effective "v2" hash, such that updates result in the more 5190Sstevel@tonic-gate * portable hash being used. 5200Sstevel@tonic-gate * 5211061Scasper * An unwanted side effect of a hash based solely on the file 5221061Scasper * meta data is the fact that we pay no attention to the contents 5231061Scasper * which may remain the same despite meta data changes. This happens 5241061Scasper * with (live) upgrades. We extend the V2 hash with an additional 5251061Scasper * digest of the file contents and the code retrieving the hash 5261061Scasper * from the repository zero fills the remainder so we can detect 5271061Scasper * it is missing. 5281061Scasper * 5291061Scasper * If the the V2 digest matches, we check for the presence of 5301061Scasper * the contents digest and compute and store it if missing. 5311061Scasper * 5321061Scasper * If the V2 digest doesn't match but we also have a non-zero 5331061Scasper * file hash, we match the file content digest. If it matches, 5341061Scasper * we compute and store the new complete hash so that later 5351061Scasper * checks will find the meta data digest correct. 5361061Scasper * 5371061Scasper * If the above matches fail and the V1 hash doesn't match either, 5381061Scasper * we consider the test to have failed, implying that some aspect 5391061Scasper * of the manifest has changed. 5400Sstevel@tonic-gate */ 5410Sstevel@tonic-gate 5420Sstevel@tonic-gate cp = getenv("SVCCFG_CHECKHASH"); 5430Sstevel@tonic-gate do_hash = (cp != NULL && *cp != '\0'); 5440Sstevel@tonic-gate if (!do_hash) { 5452462Scasper if (pnamep != NULL) 5462462Scasper *pnamep = NULL; 5472462Scasper return (MHASH_NEWFILE); 5482462Scasper } 5492462Scasper 5507475SPhilippe.Jung@Sun.COM pname = mhash_filename_to_propname(file, B_FALSE); 5512462Scasper if (pname == NULL) 5522462Scasper return (MHASH_FAILURE); 5532462Scasper 5542462Scasper hashash = mhash_retrieve_entry(hndl, pname, stored_hash) == 0; 5552462Scasper 5562462Scasper /* Never reread a profile. */ 5572462Scasper if (hashash && is_profile) { 5582462Scasper uu_free(pname); 5592462Scasper return (MHASH_RECONCILED); 5602462Scasper } 5612462Scasper 5622462Scasper /* 5632462Scasper * No hash and not interested in one, then don't bother computing it. 5642462Scasper * We also skip returning the property name in that case. 5652462Scasper */ 5662462Scasper if (!hashash && hashbuf == NULL) { 5672462Scasper uu_free(pname); 5682462Scasper return (MHASH_NEWFILE); 5690Sstevel@tonic-gate } 5700Sstevel@tonic-gate 5717475SPhilippe.Jung@Sun.COM do { 5720Sstevel@tonic-gate ret = stat64(file, &st); 5737475SPhilippe.Jung@Sun.COM } while (ret < 0 && errno == EINTR); 5740Sstevel@tonic-gate if (ret < 0) { 5752462Scasper uu_free(pname); 5762462Scasper return (MHASH_FAILURE); 5770Sstevel@tonic-gate } 5780Sstevel@tonic-gate 5790Sstevel@tonic-gate data = uu_msprintf(MHASH_FORMAT_V2, st.st_uid, st.st_gid, 5800Sstevel@tonic-gate st.st_size, st.st_mtime); 5810Sstevel@tonic-gate if (data == NULL) { 5822462Scasper uu_free(pname); 5832462Scasper return (MHASH_FAILURE); 5840Sstevel@tonic-gate } 5850Sstevel@tonic-gate 5861061Scasper (void) memset(hash, 0, MHASH_SIZE); 5870Sstevel@tonic-gate md5_calc(hash, (uchar_t *)data, strlen(data)); 5880Sstevel@tonic-gate 5890Sstevel@tonic-gate uu_free(data); 5900Sstevel@tonic-gate 5912462Scasper /* 5922462Scasper * Verify the meta data hash. 5932462Scasper */ 5942462Scasper if (hashash && memcmp(hash, stored_hash, MD5_DIGEST_LENGTH) == 0) { 5952462Scasper int i; 5962462Scasper 5972462Scasper metahashok = 1; 5982462Scasper /* 5992462Scasper * The metadata hash matches; now we see if there was a 6002462Scasper * content hash; if not, we will continue on and compute and 6012462Scasper * store the updated hash. 6022462Scasper * If there was no content hash, mhash_retrieve_entry() 6032462Scasper * will have zero filled it. 6042462Scasper */ 6052462Scasper for (i = 0; i < MD5_DIGEST_LENGTH; i++) { 6062462Scasper if (stored_hash[MD5_DIGEST_LENGTH+i] != 0) { 6072462Scasper uu_free(pname); 6082462Scasper return (MHASH_RECONCILED); 6092462Scasper } 6102462Scasper } 6112462Scasper } 6120Sstevel@tonic-gate 6132462Scasper /* 6142462Scasper * Compute the file hash as we can no longer avoid having to know it. 6152462Scasper * Note: from this point on "hash" contains the full, current, hash. 6162462Scasper */ 6172462Scasper if (md5_hash_file(file, st.st_size, &hash[MHASH_SIZE_OLD]) != 0) { 6182462Scasper uu_free(pname); 6192462Scasper return (MHASH_FAILURE); 6202462Scasper } 6212462Scasper if (hashash) { 6222462Scasper uchar_t hash_v1[MHASH_SIZE_OLD]; 6230Sstevel@tonic-gate 6242462Scasper if (metahashok || 6252462Scasper memcmp(&hash[MHASH_SIZE_OLD], &stored_hash[MHASH_SIZE_OLD], 6262462Scasper MD5_DIGEST_LENGTH) == 0) { 6272462Scasper 6282462Scasper /* 6292462Scasper * Reconcile entry: we get here when either the 6302462Scasper * meta data hash matches or the content hash matches; 6312462Scasper * we then update the database with the complete 6322462Scasper * new hash so we can be a bit quicker next time. 6332462Scasper */ 634*10461SSean.Wilcox@Sun.COM (void) mhash_store_entry(hndl, pname, file, hash, NULL); 6351061Scasper uu_free(pname); 6362462Scasper return (MHASH_RECONCILED); 6370Sstevel@tonic-gate } 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate /* 6402462Scasper * No match on V2 hash or file content; compare V1 hash. 6410Sstevel@tonic-gate */ 6420Sstevel@tonic-gate data = uu_msprintf(MHASH_FORMAT_V1, st.st_ino, st.st_uid, 6430Sstevel@tonic-gate st.st_size, st.st_mtime); 6441061Scasper if (data == NULL) { 6451061Scasper uu_free(pname); 6462462Scasper return (MHASH_FAILURE); 6471061Scasper } 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate md5_calc(hash_v1, (uchar_t *)data, strlen(data)); 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate uu_free(data); 6520Sstevel@tonic-gate 6531061Scasper if (memcmp(hash_v1, stored_hash, MD5_DIGEST_LENGTH) == 0) { 6542462Scasper /* 6552462Scasper * Update the new entry so we don't have to go through 6562462Scasper * all this trouble next time. 6572462Scasper */ 658*10461SSean.Wilcox@Sun.COM (void) mhash_store_entry(hndl, pname, file, hash, NULL); 6591061Scasper uu_free(pname); 6602462Scasper return (MHASH_RECONCILED); 6611061Scasper } 6620Sstevel@tonic-gate } 6630Sstevel@tonic-gate 6642462Scasper if (pnamep != NULL) 6652462Scasper *pnamep = pname; 6662462Scasper else 6672462Scasper uu_free(pname); 6680Sstevel@tonic-gate 6692462Scasper if (hashbuf != NULL) 6702462Scasper (void) memcpy(hashbuf, hash, MHASH_SIZE); 6712462Scasper 6722462Scasper return (MHASH_NEWFILE); 6730Sstevel@tonic-gate } 674