From patchwork Wed Oct 9 20:21:56 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 3011441 Return-Path: X-Original-To: patchwork-linux-nfs@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 133009F1E1 for ; Wed, 9 Oct 2013 20:22:14 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E378A200E8 for ; Wed, 9 Oct 2013 20:22:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6DB38200E0 for ; Wed, 9 Oct 2013 20:22:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754871Ab3JIUWF (ORCPT ); Wed, 9 Oct 2013 16:22:05 -0400 Received: from mail-qc0-f173.google.com ([209.85.216.173]:48328 "EHLO mail-qc0-f173.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755990Ab3JIUWE (ORCPT ); Wed, 9 Oct 2013 16:22:04 -0400 Received: by mail-qc0-f173.google.com with SMTP id l13so534120qcy.4 for ; Wed, 09 Oct 2013 13:22:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=0I7ONLHBpVB9vmF+XS9gswSEarud7PuWqcHjfEGhGsg=; b=kMt1rdCYy9K3REfVc7ACvuz1JfRhtjD1T1GQyRQhXYtxgGOyvgJFESj6mJrtEXRhcY dxmM+sF2i2tIHQFz/+AMkuPQNETR9YTVXeoolwXwPS53Kzs9zZOZPBgteURXUcBDV21y zRk3Ktf9bGE2r5ErimaSyYDIWF897bTLpL44KdbZ5ZjcF4E5aqeI1t8JYXSKBmUonaLG zjvwRnuGgTie6QU4bHBNVTfCROTZCuUrZ9peRKbCYtHf7sm0efRYLEi8wjeL5UuSvSzk UgOADECCUF+rBrFun0IeQj9I51A46hw2HggJR3rOGWXvOAe4bFodbxsgy8Oz1HmIIQtE m3Dw== X-Gm-Message-State: ALoCoQluTlszcKXxMt0r1MJAkWFNe2tF+vyv7Hj5a0pt7uSmcJUrI2MrDQTxockGhdw5Ml7rQRVT X-Received: by 10.229.109.193 with SMTP id k1mr1044432qcp.9.1381350123584; Wed, 09 Oct 2013 13:22:03 -0700 (PDT) Received: from salusa.poochiereds.net (cpe-107-015-124-230.nc.res.rr.com. [107.15.124.230]) by mx.google.com with ESMTPSA id n7sm89977589qai.1.1969.12.31.16.00.00 (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Wed, 09 Oct 2013 13:22:03 -0700 (PDT) From: Jeff Layton To: steved@redhat.com Cc: ssorce@redhat.com, linux-nfs@vger.kernel.org Subject: [PATCH v3 2/2] gssd: do a more thorough change of identity after forking Date: Wed, 9 Oct 2013 16:21:56 -0400 Message-Id: <1381350116-10464-3-git-send-email-jlayton@redhat.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1381350116-10464-1-git-send-email-jlayton@redhat.com> References: <1381350116-10464-1-git-send-email-jlayton@redhat.com> Sender: linux-nfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org X-Spam-Status: No, score=-7.1 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The part of process_krb5_upcall that handles non-machine user creds first tries to query GSSAPI for credentials. If that fails, it then falls back to trawling through likely credcache locations to find them and then points $KRB5CCNAME at it before proceeding. There are a number of bugs in this code that this patch attempts to address. The code that queries GSSAPI for credentials does it as root which almost universally fails to do anything useful unless we happen to be looking for non-machine root creds. Because of this, gssd almost always falls back to having to search for credcaches "manually". The code that handles credential switching is in create_auth_rpc_client, so it's too late to be of any use here. Worse yet, for historical reasons the MIT krb5 authors used %{uid} in the default credcache locations which translates to the real uid. Thus switching the fsuid or even euid is insufficient. You must switch the real uid in order to be able to find the proper credcache in most cases. This patch moves the credential switching to occur much earlier in the process and has it do a much more thorough job of it. It first drops all supplimentary groups, then determines a gid to use and switches the gids and uids to the correct ones. If it can't determine the correct gid to use, it then tries to look up the one for "nobody" and uses that. Once the credentials are switched, the forked child now no longer tries to change them back. It does the downcall with the new credentials and just exits when it's done. Signed-off-by: Jeff Layton --- utils/gssd/gssd_proc.c | 82 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index 99537d9..b48d163 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -834,7 +834,6 @@ create_auth_rpc_client(struct clnt_info *clp, CLIENT *rpc_clnt = NULL; struct rpc_gss_sec sec; AUTH *auth = NULL; - uid_t save_uid = -1; int retval = -1; OM_uint32 min_stat; char rpc_errmsg[1024]; @@ -843,16 +842,6 @@ create_auth_rpc_client(struct clnt_info *clp, struct sockaddr *addr = (struct sockaddr *) &clp->addr; socklen_t salen; - /* Create the context as the user (not as root) */ - save_uid = geteuid(); - if (setfsuid(uid) != 0) { - printerr(0, "WARNING: Failed to setfsuid for " - "user with uid %d\n", uid); - goto out_fail; - } - printerr(2, "creating context using fsuid %d (save_uid %d)\n", - uid, save_uid); - sec.qop = GSS_C_QOP_DEFAULT; sec.svc = RPCSEC_GSS_SVC_NONE; sec.cred = cred; @@ -951,11 +940,6 @@ create_auth_rpc_client(struct clnt_info *clp, out: if (sec.cred != GSS_C_NO_CREDENTIAL) gss_release_cred(&min_stat, &sec.cred); - /* Restore euid to original value */ - if (((int)save_uid != -1) && (setfsuid(save_uid) != (int)uid)) { - printerr(0, "WARNING: Failed to restore fsuid" - " to uid %d from %d\n", save_uid, uid); - } return retval; out_fail: @@ -966,6 +950,64 @@ create_auth_rpc_client(struct clnt_info *clp, } /* + * Create the context as the user (not as root). + * + * Note that we change the *real* uid here, as changing the effective uid is + * not sufficient. This is due to an unfortunate historical error in the MIT + * krb5 libs, where they used %{uid} in the default_ccache_name. Changing that + * now might break some applications so we're sort of stuck with it. + * + * Unfortunately, doing this leaves the forked child vulnerable to signals and + * renicing, but this is the best we can do. In the event that a child is + * signalled before downcalling, the kernel will just eventually time out the + * upcall attempt. + */ +static int +change_identity(uid_t uid) +{ + struct passwd *pw; + + /* drop list of supplimentary groups first */ + if (setgroups(0, NULL) != 0) { + printerr(0, "WARNING: unable to drop supplimentary groups!"); + return errno; + } + + /* try to get pwent for user */ + pw = getpwuid(uid); + if (!pw) { + /* if that doesn't work, try to get one for "nobody" */ + errno = 0; + pw = getpwnam("nobody"); + if (!pw) { + printerr(0, "WARNING: unable to determine gid for uid %u\n", uid); + return errno ? errno : ENOENT; + } + } + + /* + * Switch the GIDs. Note that we leave the saved-set-gid alone in an + * attempt to prevent attacks via ptrace() + */ + if (setresgid(pw->pw_gid, pw->pw_gid, -1) != 0) { + printerr(0, "WARNING: failed to set gid to %u!\n", pw->pw_gid); + return errno; + } + + /* + * Switch UIDs, but leave saved-set-uid alone to prevent ptrace() by + * other processes running with this uid. + */ + if (setresuid(uid, uid, -1) != 0) { + printerr(0, "WARNING: Failed to setuid for user with uid %u\n", + uid); + return errno; + } + + return 0; +} + +/* * this code uses the userland rpcsec gss library to create a krb5 * context on behalf of the kernel */ @@ -1036,6 +1078,14 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, service ? service : ""); if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && service == NULL)) { + + err = change_identity(uid); + if (err) { + printerr(0, "WARNING: failed to change identity: %s", + strerror(err)); + goto out_return_error; + } + /* Tell krb5 gss which credentials cache to use */ /* Try first to acquire credentials directly via GSSAPI */ err = gssd_acquire_user_cred(uid, &gss_cred);