From patchwork Thu Apr 28 06:53:53 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laszlo Ersek X-Patchwork-Id: 12830055 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 59500C43219 for ; Thu, 28 Apr 2022 06:54:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244625AbiD1G5Y (ORCPT ); Thu, 28 Apr 2022 02:57:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59820 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239616AbiD1G5X (ORCPT ); Thu, 28 Apr 2022 02:57:23 -0400 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id A7801986D8 for ; Wed, 27 Apr 2022 23:54:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1651128848; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qYh2UpiXcDz+kzKDBrdeASsBkN1+PxOtmswS+LHS+Ng=; b=gPEjpMGLRTMOqrXDqeo/mviOv+z3swgSp6KVCVXfGxKEWLA7E1g4v5w+bwW/AGb96WjB6A dQVR+ul7ewTeaFhmP65VXwCSmzNsZpodWvcontwAdlsFzLHz1mJdEA3FsbFZQgVSzXENkZ +iwoxpQCA3b32BI8go5dOEYw2Vd1C5Q= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-484-1YbROI4-P9i0Kefl4LgWzA-1; Thu, 28 Apr 2022 02:54:07 -0400 X-MC-Unique: 1YbROI4-P9i0Kefl4LgWzA-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 09163858EEC for ; Thu, 28 Apr 2022 06:54:07 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (unknown [10.39.192.29]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1D8EF416385; Thu, 28 Apr 2022 06:54:05 +0000 (UTC) From: Laszlo Ersek To: SELinux List , Laszlo Ersek Cc: "Richard W.M. Jones" , Petr Lautrbach Subject: [PATCH for-3.5 4/5] selinux_restorecon: introduce SELINUX_RESTORECON_COUNT_ERRORS Date: Thu, 28 Apr 2022 08:53:53 +0200 Message-Id: <20220428065354.27605-5-lersek@redhat.com> In-Reply-To: <20220428065354.27605-1-lersek@redhat.com> References: <20220428065354.27605-1-lersek@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.85 on 10.11.54.10 Precedence: bulk List-ID: X-Mailing-List: selinux@vger.kernel.org Currently, if the SELINUX_RESTORECON_ABORT_ON_ERROR flag is clear, then selinux_restorecon[_parallel]() does not abort the file tree walk upon an error, but the function itself fails the same, with the same (-1) return value. This in turn is reported by the setfiles(8) utility to its parent process with the same exit code (255). In libguestfs we want to proceed after setfiles(8) fails *at most* with such errors that occur during the file tree walk. We need setfiles(8) to exit with a distinct exit status in that situation. For this, introduce the SELINUX_RESTORECON_COUNT_ERRORS flag, and the corresponding selinux_restorecon_get_skipped_errors() function, for selinux_restorecon[_parallel]() to count, but otherwise ignore, errors during the file tree walk. When no other kind of error occurs, the relabeling functions will return zero, and the caller can fetch the number of errors ignored during the file tree walk with selinux_restorecon_get_skipped_errors(). Importantly, when at least one such error is skipped, we don't write partial match digests for subdirectories, as any masked error means that any subdirectory may not have been completely relabeled. Cc: "Richard W.M. Jones" Cc: Petr Lautrbach Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1794518 Signed-off-by: Laszlo Ersek --- libselinux/include/selinux/restorecon.h | 15 +++++++++ libselinux/src/selinux_restorecon.c | 34 +++++++++++++++++--- libselinux/man/man3/selinux_restorecon.3 | 22 ++++++++++++- libselinux/man/man3/selinux_restorecon_get_skipped_errors.3 | 28 ++++++++++++++++ libselinux/src/libselinux.map | 5 +++ 5 files changed, 98 insertions(+), 6 deletions(-) diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h index 1821a3dc596c..b10fe684eff9 100644 --- a/libselinux/include/selinux/restorecon.h +++ b/libselinux/include/selinux/restorecon.h @@ -121,6 +121,11 @@ extern int selinux_restorecon_parallel(const char *pathname, */ #define SELINUX_RESTORECON_CONFLICT_ERROR 0x10000 +/* + * Count, but otherwise ignore, errors during the file tree walk. + */ +#define SELINUX_RESTORECON_COUNT_ERRORS 0x20000 + /** * selinux_restorecon_set_sehandle - Set the global fc handle. * @hndl: specifies handle to set as the global fc handle. @@ -205,6 +210,16 @@ extern int selinux_restorecon_xattr(const char *pathname, /* Do not read /proc/mounts. */ #define SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS 0x0008 +/* selinux_restorecon_get_skipped_errors - Get the number of errors ignored + * during re-labeling. + * + * If SELINUX_RESTORECON_COUNT_ERRORS was passed to selinux_restorecon(3) or + * selinux_restorecon_parallel(3), and that function returned successfully + * (i.e., with a zero return value), then this function returns the number of + * errors ignored during the file tree walk. + */ +extern long unsigned selinux_restorecon_get_skipped_errors(void); + #ifdef __cplusplus } #endif diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c index 72f4fb462e34..e61929120a74 100644 --- a/libselinux/src/selinux_restorecon.c +++ b/libselinux/src/selinux_restorecon.c @@ -66,6 +66,9 @@ static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER; static struct dir_xattr *dir_xattr_list; static struct dir_xattr *dir_xattr_last; +/* Number of errors ignored during the file tree walk. */ +static long unsigned skipped_errors; + /* restorecon_flags for passing to restorecon_sb() */ struct rest_flags { bool nochange; @@ -83,6 +86,7 @@ struct rest_flags { bool ignore_noent; bool warnonnomatch; bool conflicterror; + bool count_errors; }; static void restorecon_init(void) @@ -827,6 +831,7 @@ struct rest_state { struct dir_hash_node *head, *current; bool abort; int error; + long unsigned skipped_errors; int saved_errno; pthread_mutex_t mutex; }; @@ -949,11 +954,17 @@ loop_body: goto unlock; } - state->error |= error; first = false; - if (error && state->flags.abort_on_error) { - state->abort = true; - goto finish; + if (error) { + if (state->flags.abort_on_error) { + state->error = error; + state->abort = true; + goto finish; + } + if (state->flags.count_errors) + state->skipped_errors++; + else + state->error = error; } break; } @@ -1007,12 +1018,15 @@ static int selinux_restorecon_common(const char *pathname_orig, SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false; state.ignore_digest = (restorecon_flags & SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false; + state.flags.count_errors = (restorecon_flags & + SELINUX_RESTORECON_COUNT_ERRORS) ? true : false; state.setrestorecondigest = true; state.head = NULL; state.current = NULL; state.abort = false; state.error = 0; + state.skipped_errors = 0; state.saved_errno = 0; struct stat sb; @@ -1225,8 +1239,11 @@ static int selinux_restorecon_common(const char *pathname_orig, /* * Labeling successful. Write partial match digests for subdirectories. * TODO: Write digest upon FTS_DP if no error occurs in its descents. + * Note: we can't ignore errors here that we've masked due to + * SELINUX_RESTORECON_COUNT_ERRORS. */ - if (state.setrestorecondigest && !state.flags.nochange && !error) { + if (state.setrestorecondigest && !state.flags.nochange && !error && + state.skipped_errors == 0) { current = state.head; while (current != NULL) { if (setxattr(current->path, @@ -1241,6 +1258,8 @@ static int selinux_restorecon_common(const char *pathname_orig, } } + skipped_errors = state.skipped_errors; + out: if (state.flags.progress && state.flags.mass_relabel) fprintf(stdout, "\r%s 100.0%%\n", pathname); @@ -1520,3 +1539,8 @@ cleanup: } return -1; } + +long unsigned selinux_restorecon_get_skipped_errors(void) +{ + return skipped_errors; +} diff --git a/libselinux/man/man3/selinux_restorecon.3 b/libselinux/man/man3/selinux_restorecon.3 index 334d2930bb4f..218aaf6d2ae5 100644 --- a/libselinux/man/man3/selinux_restorecon.3 +++ b/libselinux/man/man3/selinux_restorecon.3 @@ -78,7 +78,10 @@ specfile entries SHA1 digest. The specfile entries digest will be written to the .IR security.sehash extended attribute once relabeling has been completed successfully provided the .B SELINUX_RESTORECON_NOCHANGE -flag has not been set. +flag has not been set, and no errors have been skipped during the file tree walk +due to the +.B SELINUX_RESTORECON_COUNT_ERRORS +flag. .sp .B SELINUX_RESTORECON_NOCHANGE don't change any file labels (passive check) or update the digest in the @@ -164,6 +167,21 @@ on a directory below this. .B SELINUX_RESTORECON_CONFLICT_ERROR to treat conflicting specifications, such as where two hardlinks for the same inode have different contexts, as errors. +.sp +.B SELINUX_RESTORECON_COUNT_ERRORS +Count, but otherwise ignore, errors during the file tree walk. Only makes a +difference if the +.B SELINUX_RESTORECON_ABORT_ON_ERROR +flag is clear. Call +.BR selinux_restorecon_get_skipped_errors (3) +for fetching the ignored (skipped) error count after +.BR selinux_restorecon (3) +or +.BR selinux_restorecon_parallel (3) +completes with success. In case any errors were skipped during the file tree +walk, the specfile entries SHA1 digest will not have been written to the +.IR security.sehash +extended attribute. .RE .sp The behavior regarding the checking and updating of the SHA1 digest described @@ -279,6 +297,8 @@ option. .br .BR selinux_restorecon_default_handle (3), .br +.BR selinux_restorecon_get_skipped_errors (3), +.br .BR selinux_restorecon_set_exclude_list (3), .br .BR selinux_restorecon_set_alt_rootpath (3), diff --git a/libselinux/man/man3/selinux_restorecon_get_skipped_errors.3 b/libselinux/man/man3/selinux_restorecon_get_skipped_errors.3 new file mode 100644 index 000000000000..d1757b7612ab --- /dev/null +++ b/libselinux/man/man3/selinux_restorecon_get_skipped_errors.3 @@ -0,0 +1,28 @@ +.TH "selinux_restorecon_get_skipped_errors" "3" "27 Apr 2022" "Security Enhanced Linux" "SELinux API documentation" + +.SH "NAME" +selinux_restorecon_get_skipped_errors \- get the number of errors ignored by +.BR selinux_restorecon (3) +or +.BR selinux_restorecon_parallel (3) +during the file tree walk +. +.SH "SYNOPSIS" +.B #include +.sp +.BI "long unsigned selinux_restorecon_get_skipped_errors(void);" +.in +\w'long unsigned selinux_restorecon_get_skipped_errors('u +. +.SH "DESCRIPTION" +If +.B SELINUX_RESTORECON_COUNT_ERRORS +was passed to +.BR selinux_restorecon (3) +or +.BR selinux_restorecon_parallel (3) +and that function returned successfully (i.e., with a zero return value), then +.BR selinux_restorecon_get_skipped_errors () +returns the number of errors ignored during the file tree walk. +. +.SH "SEE ALSO" +.BR selinux_restorecon (3) diff --git a/libselinux/src/libselinux.map b/libselinux/src/libselinux.map index 4acf1caacb55..43631f0965fe 100644 --- a/libselinux/src/libselinux.map +++ b/libselinux/src/libselinux.map @@ -245,3 +245,8 @@ LIBSELINUX_3.4 { global: selinux_restorecon_parallel; } LIBSELINUX_1.0; + +LIBSELINUX_3.5 { + global: + selinux_restorecon_get_skipped_errors; +} LIBSELINUX_3.4;