4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
27 #include <sys/types.h>
45 #include <manifest_hash.h>
48 * Translate a file name to property name. Return an allocated string or NULL
49 * if realpath() fails. If deathrow is true, realpath() is skipped. This
50 * allows to return the property name even if the file doesn't exist.
53 mhash_filename_to_propname(const char *in
, boolean_t deathrow
)
55 char *out
, *cp
, *base
;
56 size_t len
, piece_len
;
59 out
= uu_zalloc(PATH_MAX
+ 1);
61 /* used only for service deathrow handling */
62 if (strlcpy(out
, in
, PATH_MAX
+ 1) >= (PATH_MAX
+ 1)) {
67 if (realpath(in
, out
) == NULL
) {
73 base
= getenv("PKG_INSTALL_ROOT");
76 * We copy-shift over the basedir and the leading slash, since it's
77 * not relevant to when we boot with this repository.
80 if (base
!= NULL
&& strncmp(out
, base
, strlen(base
)) == 0)
81 base_sz
= strlen(base
);
86 (void) memmove(out
, cp
, strlen(cp
) + 1);
89 if (len
> scf_limit(SCF_LIMIT_MAX_NAME_LENGTH
)) {
90 /* Use the first half and the second half. */
91 piece_len
= (scf_limit(SCF_LIMIT_MAX_NAME_LENGTH
) - 3) / 2;
93 (void) strncpy(out
+ piece_len
, "__", 2);
95 (void) memmove(out
+ piece_len
+ 2, out
+ (len
- piece_len
),
100 * Translate non-property characters to '_', first making sure that
101 * we don't begin with '_'.
107 for (cp
= out
+ 1; *cp
!= '\0'; ++cp
) {
108 if (!(isalnum(*cp
) || *cp
== '_' || *cp
== '-'))
116 mhash_retrieve_entry(scf_handle_t
*hndl
, const char *name
, uchar_t
*hash
,
117 apply_action_t
*action
)
121 scf_propertygroup_t
*pg
;
122 scf_property_t
*prop
;
129 *action
= APPLY_NONE
;
132 * In this implementation the hash for name is the opaque value of
133 * svc:/MHASH_SVC/:properties/name/MHASH_PROP
136 if ((scope
= scf_scope_create(hndl
)) == NULL
||
137 (svc
= scf_service_create(hndl
)) == NULL
||
138 (pg
= scf_pg_create(hndl
)) == NULL
||
139 (prop
= scf_property_create(hndl
)) == NULL
||
140 (val
= scf_value_create(hndl
)) == NULL
) {
145 if (scf_handle_get_local_scope(hndl
, scope
) < 0) {
150 if (scf_scope_get_service(scope
, MHASH_SVC
, svc
) < 0) {
155 if (scf_service_get_pg(svc
, name
, pg
) != SCF_SUCCESS
) {
160 if (scf_pg_get_property(pg
, MHASH_PROP
, prop
) != SCF_SUCCESS
) {
165 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
170 szret
= scf_value_get_opaque(val
, hash
, MHASH_SIZE
);
177 * Make sure that the old hash is returned with
178 * remainder of the bytes zeroed.
180 if (szret
== MHASH_SIZE_OLD
) {
181 (void) memset(hash
+ MHASH_SIZE_OLD
, 0,
182 MHASH_SIZE
- MHASH_SIZE_OLD
);
183 } else if (szret
!= MHASH_SIZE
) {
184 scf_value_destroy(val
);
190 * If caller has requested the apply_last property, read the
191 * property if it exists.
193 if (action
!= NULL
) {
196 if (scf_pg_get_property(pg
, MHASH_APPLY_PROP
, prop
) !=
199 if ((err
!= SCF_ERROR_DELETED
) &&
200 (err
!= SCF_ERROR_NOT_FOUND
)) {
205 if (scf_property_get_value(prop
, val
) != SCF_SUCCESS
) {
207 if ((err
!= SCF_ERROR_DELETED
) &&
208 (err
!= SCF_ERROR_NOT_FOUND
)) {
213 if (scf_value_get_boolean(val
, &apply_value
) != SCF_SUCCESS
) {
218 *action
= APPLY_LATE
;
222 (void) scf_value_destroy(val
);
223 scf_property_destroy(prop
);
225 scf_service_destroy(svc
);
226 scf_scope_destroy(scope
);
232 mhash_store_entry(scf_handle_t
*hndl
, const char *name
, const char *fname
,
233 uchar_t
*hash
, apply_action_t apply_late
, char **errstr
)
235 scf_scope_t
*scope
= NULL
;
236 scf_service_t
*svc
= NULL
;
237 scf_propertygroup_t
*pg
= NULL
;
238 scf_property_t
*prop
= NULL
;
239 scf_value_t
*aval
= NULL
;
240 scf_value_t
*val
= NULL
;
241 scf_value_t
*fval
= NULL
;
242 scf_transaction_t
*tx
= NULL
;
243 scf_transaction_entry_t
*ae
= NULL
;
244 scf_transaction_entry_t
*e
= NULL
;
245 scf_transaction_entry_t
*fe
= NULL
;
251 if ((scope
= scf_scope_create(hndl
)) == NULL
||
252 (svc
= scf_service_create(hndl
)) == NULL
||
253 (pg
= scf_pg_create(hndl
)) == NULL
||
254 (prop
= scf_property_create(hndl
)) == NULL
) {
256 *errstr
= gettext("Could not create scf objects");
261 if (scf_handle_get_local_scope(hndl
, scope
) != SCF_SUCCESS
) {
263 *errstr
= gettext("Could not get local scope");
268 for (i
= 0; i
< 5; ++i
) {
270 if (scf_scope_get_service(scope
, MHASH_SVC
, svc
) ==
274 if (scf_error() != SCF_ERROR_NOT_FOUND
) {
276 *errstr
= gettext("Could not get manifest hash "
282 if (scf_scope_add_service(scope
, MHASH_SVC
, svc
) ==
288 if (err
== SCF_ERROR_EXISTS
)
291 else if (err
== SCF_ERROR_PERMISSION_DENIED
) {
293 *errstr
= gettext("Could not store file hash: "
294 "permission denied.\n");
300 *errstr
= gettext("Could not add manifest hash "
308 *errstr
= gettext("Could not store file hash: "
309 "service addition contention.\n");
314 for (i
= 0; i
< 5; ++i
) {
315 if (scf_service_get_pg(svc
, name
, pg
) == SCF_SUCCESS
)
318 if (scf_error() != SCF_ERROR_NOT_FOUND
) {
320 *errstr
= gettext("Could not get service's "
326 if (scf_service_add_pg(svc
, name
, MHASH_PG_TYPE
,
327 MHASH_PG_FLAGS
, pg
) == SCF_SUCCESS
)
332 if (err
== SCF_ERROR_EXISTS
)
335 else if (err
== SCF_ERROR_PERMISSION_DENIED
) {
337 *errstr
= gettext("Could not store file hash: "
338 "permission denied.\n");
344 *errstr
= gettext("Could not store file hash");
350 *errstr
= gettext("Could not store file hash: "
351 "property group addition contention.\n");
356 if ((e
= scf_entry_create(hndl
)) == NULL
||
357 (val
= scf_value_create(hndl
)) == NULL
||
358 (fe
= scf_entry_create(hndl
)) == NULL
||
359 (fval
= scf_value_create(hndl
)) == NULL
||
360 (ae
= scf_entry_create(hndl
)) == NULL
||
361 (aval
= scf_value_create(hndl
)) == NULL
) {
363 *errstr
= gettext("Could not store file hash: "
364 "permission denied.\n");
369 ret
= scf_value_set_opaque(val
, hash
, MHASH_SIZE
);
370 assert(ret
== SCF_SUCCESS
);
371 ret
= scf_value_set_astring(fval
, fname
);
372 assert(ret
== SCF_SUCCESS
);
373 if (apply_late
== APPLY_LATE
) {
374 scf_value_set_boolean(aval
, 1);
377 tx
= scf_transaction_create(hndl
);
380 *errstr
= gettext("Could not create transaction");
386 if (scf_pg_update(pg
) == -1) {
388 *errstr
= gettext("Could not update hash "
393 if (scf_transaction_start(tx
, pg
) != SCF_SUCCESS
) {
394 if (scf_error() != SCF_ERROR_PERMISSION_DENIED
) {
396 *errstr
= gettext("Could not start "
397 "hash transaction.\n");
403 *errstr
= gettext("Could not store file hash: "
404 "permission denied.\n");
407 scf_transaction_destroy(tx
);
408 (void) scf_entry_destroy(e
);
412 if (scf_transaction_property_new(tx
, e
, MHASH_PROP
,
413 SCF_TYPE_OPAQUE
) != SCF_SUCCESS
&&
414 scf_transaction_property_change_type(tx
, e
, MHASH_PROP
,
415 SCF_TYPE_OPAQUE
) != SCF_SUCCESS
) {
417 *errstr
= gettext("Could not modify hash "
423 ret
= scf_entry_add_value(e
, val
);
424 assert(ret
== SCF_SUCCESS
);
426 if (scf_transaction_property_new(tx
, fe
, MHASH_FILE_PROP
,
427 SCF_TYPE_ASTRING
) != SCF_SUCCESS
&&
428 scf_transaction_property_change_type(tx
, fe
,
429 MHASH_FILE_PROP
, SCF_TYPE_ASTRING
) != SCF_SUCCESS
) {
431 *errstr
= gettext("Could not modify file "
437 ret
= scf_entry_add_value(fe
, fval
);
438 assert(ret
== SCF_SUCCESS
);
440 switch (apply_late
) {
442 if (scf_transaction_property_delete(tx
, ae
,
443 MHASH_APPLY_PROP
) != 0) {
445 if ((err
!= SCF_ERROR_DELETED
) &&
446 (err
!= SCF_ERROR_NOT_FOUND
)) {
447 if (errstr
!= NULL
) {
448 *errstr
= gettext("Could not "
458 if ((scf_transaction_property_new(tx
, ae
,
460 SCF_TYPE_BOOLEAN
) != SCF_SUCCESS
) &&
461 (scf_transaction_property_change_type(tx
, ae
,
462 MHASH_APPLY_PROP
, SCF_TYPE_BOOLEAN
) !=
464 if (errstr
!= NULL
) {
465 *errstr
= gettext("Could not modify "
466 "apply_late property");
472 ret
= scf_entry_add_value(ae
, aval
);
473 assert(ret
== SCF_SUCCESS
);
479 ret
= scf_transaction_commit(tx
);
482 scf_transaction_reset(tx
);
486 if (scf_error() != SCF_ERROR_PERMISSION_DENIED
) {
488 *errstr
= gettext("Could not store file hash: "
489 "permission denied.\n");
495 *errstr
= gettext("Could not commit transaction");
499 scf_transaction_destroy(tx
);
500 (void) scf_entry_destroy(e
);
501 (void) scf_entry_destroy(fe
);
502 (void) scf_entry_destroy(ae
);
505 (void) scf_value_destroy(val
);
506 (void) scf_value_destroy(fval
);
507 (void) scf_value_destroy(aval
);
508 scf_property_destroy(prop
);
510 scf_service_destroy(svc
);
511 scf_scope_destroy(scope
);
517 * Generate the md5 hash of a file; manifest files are smallish
518 * so we can read them in one gulp.
521 md5_hash_file(const char *file
, off64_t sz
, uchar_t
*hash
)
528 fd
= open(file
, O_RDONLY
);
538 res
= read(fd
, buf
, (size_t)sz
);
544 md5_calc(hash
, (uchar_t
*)buf
, (unsigned int) sz
);
554 * int mhash_test_file(scf_handle_t *, const char *, uint_t, char **, uchar_t *)
555 * Test the given filename against the hashed metadata in the repository.
556 * The behaviours for import and apply are slightly different. For imports,
557 * if the hash value is absent or different, then the import operation
558 * continues. For profile application, the operation continues only if the
559 * hash value for the file is absent.
561 * We keep two hashes: one which can be quickly test: the metadata hash,
562 * and one which is more expensive to test: the file contents hash.
564 * If either hash matches, the file does not need to be re-read.
565 * If only one of the hashes matches, a side effect of this function
566 * is to store the newly computed hash.
567 * If neither hash matches, the hash computed for the new file is returned
571 * MHASH_NEWFILE - the file no longer matches the hash or no hash existed
572 * ONLY in this case we return the new file's hash.
573 * MHASH_FAILURE - an internal error occurred, or the file was not found.
574 * MHASH_RECONCILED- based on the metadata/file hash, the file does
575 * not need to be re-read; if necessary,
576 * the hash was upgraded or reconciled.
578 * NOTE: no hash is returned UNLESS MHASH_NEWFILE is returned.
581 mhash_test_file(scf_handle_t
*hndl
, const char *file
, uint_t is_profile
,
582 char **pnamep
, uchar_t
*hashbuf
)
584 apply_action_t action
;
589 uchar_t stored_hash
[MHASH_SIZE
];
590 uchar_t hash
[MHASH_SIZE
];
600 * In the case where we are doing automated imports, we reduce the UID,
601 * the GID, the size, and the mtime into a string (to eliminate
602 * endianness) which we then make opaque as a single MD5 digest.
604 * The previous hash was composed of the inode number, the UID, the file
605 * size, and the mtime. This formulation was found to be insufficiently
606 * portable for use in highly replicated deployments. The current
607 * algorithm will allow matches of this "v1" hash, but always returns
608 * the effective "v2" hash, such that updates result in the more
609 * portable hash being used.
611 * An unwanted side effect of a hash based solely on the file
612 * meta data is the fact that we pay no attention to the contents
613 * which may remain the same despite meta data changes. This happens
614 * with (live) upgrades. We extend the V2 hash with an additional
615 * digest of the file contents and the code retrieving the hash
616 * from the repository zero fills the remainder so we can detect
619 * If the the V2 digest matches, we check for the presence of
620 * the contents digest and compute and store it if missing.
622 * If the V2 digest doesn't match but we also have a non-zero
623 * file hash, we match the file content digest. If it matches,
624 * we compute and store the new complete hash so that later
625 * checks will find the meta data digest correct.
627 * If the above matches fail and the V1 hash doesn't match either,
628 * we consider the test to have failed, implying that some aspect
629 * of the manifest has changed.
632 cp
= getenv("SVCCFG_CHECKHASH");
633 do_hash
= (cp
!= NULL
&& *cp
!= '\0');
635 return (MHASH_NEWFILE
);
638 pname
= mhash_filename_to_propname(file
, B_FALSE
);
640 return (MHASH_FAILURE
);
642 hashash
= mhash_retrieve_entry(hndl
, pname
, stored_hash
, &action
) == 0;
643 if (is_profile
== 0) {
644 /* Actions other than APPLY_NONE are restricted to profiles. */
645 assert(action
== APPLY_NONE
);
649 * As a general rule, we do not reread a profile. The exception to
650 * this rule is when we are running as part of the manifest import
651 * service and the apply_late property is set to true.
653 if (hashash
&& is_profile
) {
654 cp
= getenv("SMF_FMRI");
656 (strcmp(cp
, SCF_INSTANCE_MI
) != 0) ||
657 (action
!= APPLY_LATE
)) {
659 return (MHASH_RECONCILED
);
664 * No hash and not interested in one, then don't bother computing it.
665 * We also skip returning the property name in that case.
667 if (!hashash
&& hashbuf
== NULL
) {
669 return (MHASH_NEWFILE
);
673 ret
= stat64(file
, &st
);
674 } while (ret
< 0 && errno
== EINTR
);
677 return (MHASH_FAILURE
);
680 data
= uu_msprintf(MHASH_FORMAT_V2
, st
.st_uid
, st
.st_gid
,
681 st
.st_size
, st
.st_mtime
);
684 return (MHASH_FAILURE
);
687 (void) memset(hash
, 0, MHASH_SIZE
);
688 md5_calc(hash
, (uchar_t
*)data
, strlen(data
));
693 * Verify the meta data hash.
695 if (hashash
&& memcmp(hash
, stored_hash
, MD5_DIGEST_LENGTH
) == 0) {
700 * The metadata hash matches; now we see if there was a
701 * content hash; if not, we will continue on and compute and
702 * store the updated hash.
703 * If there was no content hash, mhash_retrieve_entry()
704 * will have zero filled it.
706 for (i
= 0; i
< MD5_DIGEST_LENGTH
; i
++) {
707 if (stored_hash
[MD5_DIGEST_LENGTH
+i
] != 0) {
708 if (action
== APPLY_LATE
) {
714 ret
= MHASH_RECONCILED
;
722 * Compute the file hash as we can no longer avoid having to know it.
723 * Note: from this point on "hash" contains the full, current, hash.
725 if (md5_hash_file(file
, st
.st_size
, &hash
[MHASH_SIZE_OLD
]) != 0) {
727 return (MHASH_FAILURE
);
730 uchar_t hash_v1
[MHASH_SIZE_OLD
];
733 memcmp(&hash
[MHASH_SIZE_OLD
], &stored_hash
[MHASH_SIZE_OLD
],
734 MD5_DIGEST_LENGTH
) == 0) {
737 * Reconcile entry: we get here when either the
738 * meta data hash matches or the content hash matches;
739 * we then update the database with the complete
740 * new hash so we can be a bit quicker next time.
742 (void) mhash_store_entry(hndl
, pname
, file
, hash
,
744 if (action
== APPLY_LATE
) {
750 ret
= MHASH_RECONCILED
;
756 * No match on V2 hash or file content; compare V1 hash.
758 data
= uu_msprintf(MHASH_FORMAT_V1
, st
.st_ino
, st
.st_uid
,
759 st
.st_size
, st
.st_mtime
);
762 return (MHASH_FAILURE
);
765 md5_calc(hash_v1
, (uchar_t
*)data
, strlen(data
));
769 if (memcmp(hash_v1
, stored_hash
, MD5_DIGEST_LENGTH
) == 0) {
771 * Update the new entry so we don't have to go through
772 * all this trouble next time.
774 (void) mhash_store_entry(hndl
, pname
, file
, hash
,
777 return (MHASH_RECONCILED
);
787 (void) memcpy(hashbuf
, hash
, MHASH_SIZE
);
789 return (MHASH_NEWFILE
);