From patchwork Wed Nov 17 22:34:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Volodymyr Khomenko X-Patchwork-Id: 12625677 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 mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7819EC433FE for ; Wed, 17 Nov 2021 22:34:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5926361B5F for ; Wed, 17 Nov 2021 22:34:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235033AbhKQWha (ORCPT ); Wed, 17 Nov 2021 17:37:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55260 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241281AbhKQWh3 (ORCPT ); Wed, 17 Nov 2021 17:37:29 -0500 Received: from mail-lf1-x132.google.com (mail-lf1-x132.google.com [IPv6:2a00:1450:4864:20::132]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AF88CC061570 for ; Wed, 17 Nov 2021 14:34:29 -0800 (PST) Received: by mail-lf1-x132.google.com with SMTP id t26so15695794lfk.9 for ; Wed, 17 Nov 2021 14:34:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vastdata.com; s=google; h=mime-version:from:date:message-id:subject:to; bh=rCGmULVjgfMESMgLCHWYe1/vqNfO+N2a22aZ2YfhGok=; b=QfxZWOPNPI6iPdHZV159mxBjl+4zsip6H9HMjUQ+n8vpunBzh3IEnH0YQVoe7K8hEf QZIGT/qEcUB/Z++J/b116XPkNjhBvnX9jhR4OoMgES+99xs8bdZrPl5be+q5GYSXk/m7 jQ5uReM3yUEkW50KWXRDuMxDgC/OWIK4/i/crCErNZ9c9QlsgR+GlZBupC3FVJA9cUfK BgUBM++iVUFkt4dEWcITjvRD3AOy3qQB2d7kw0gYtaBYPc/CdNwXCQZB5kcBCP+fDDyn NU2+Od6FlVVVYeovASgJU28EbvYCa4wLSQ07qAxQJKkuEeWKKRI2vK/sald9Rr5pA0JB D0HQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=rCGmULVjgfMESMgLCHWYe1/vqNfO+N2a22aZ2YfhGok=; b=T8Mfz71ix09CjqNmBHnwyAyesbp88ujSg0aaxadHLSYXRg7p6aV4zi/6+uODtmYGlt F2h+L0qD3XFuUpo0mCxYRoo7sxLfqaYcJJoQWZdb5NZZouAl1CayuGBAXFY7hC6PkuoS Qeq5tN+XEU53R6bwL5U6dwk9AfhyU08f/KVF+Z2/piK5+wCLiWuHcTA3nnUclhfpvHrA KmCp+VjgGlTIstI/Wozif6XtINdMdqEn2iyclm+Ccg53C6SLJ3hkxyOu9hea48Dnx3ML BjUidcxS53Jc+e0R+z2SIsW1iezeVUqJ+Viwif0MyI2v5A/KMf60SqMiYitA3Yh0G4y2 5rBg== X-Gm-Message-State: AOAM530TuzhQUdQ141b6aomGJaQ4O/Z1Ij9W1tHom2HSfCTH0HgQfhOd lIeFh4SyA4VXPq8ngkeyyIytXK6yBg8HFqwGcDE/6ChIaux8EQ== X-Google-Smtp-Source: ABdhPJz98FRCqrVTVedX/LLzx5omwT51kDJb8CkcT3n2jQFUbCyonxONDDKxqbaAfCupSa9mNpYN1eYepZqIqUOGnLk= X-Received: by 2002:a2e:888f:: with SMTP id k15mr11562565lji.53.1637188468020; Wed, 17 Nov 2021 14:34:28 -0800 (PST) MIME-Version: 1.0 From: Volodymyr Khomenko Date: Thu, 18 Nov 2021 00:34:16 +0200 Message-ID: Subject: pynfs: fixed gssapi usage (RPCGSS) + GSS* tests for nfs4.0 server test To: linux-nfs@vger.kernel.org, "J. Bruce Fields" , Ilan Steinberg Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org Hi linux-nfs, Please review attached patches for gssapi-related code of nfs4.0 server test (pynfs). This is a continuation of previous work to make GSS tests work correctly with the recent gssapi python library (using python3). $ nfs4.0/testserver.py server.fqdn:/export gss noGSS8 --security=krb5 ... Command line asked for 8 of 673 tests Of those: 3 Skipped, 0 Failed, 0 Warned, 5 Passed $ nfs4.0/testserver.py server.fqdn:/export GSS8 --security=krb5 ... Command line asked for 1 of 673 tests Of those: 0 Skipped, 0 Failed, 0 Warned, 1 Passed $ nfs4.0/testserver.py server.fqdn:/export gss noGSS8 --security=krb5i ... Command line asked for 8 of 673 tests Of those: 1 Skipped, 0 Failed, 0 Warned, 7 Passed Thanks, Volodymyr Khomenko. From 3f94e9aa811cfff033a8d5438b7679ce0fa55b73 Mon Sep 17 00:00:00 2001 From: Volodymyr Khomenko Date: Wed, 17 Nov 2021 23:58:20 +0200 Subject: [PATCH 1/2] Fixed gssapi usage (RPCGSS) for nfs4.0 server test gssapi library used in the code has been changed and current code is not compatible with API of new library version. Fixed the code to work with recent gssapi (tested with 1.6.2). Tested with krb5, krb5i and krb5p security, like this: nfs4.0/testserver.py server.fqdn:/export gss noGSS8 --security=krb5 Note - GSS8 is known to cause the problems for other tests that run after it, however it's safe to run it alone. Signed-off-by: Volodymyr Khomenko --- nfs4.0/lib/rpc/rpcsec/sec_auth_gss.py | 116 ++++++++++---------------- 1 file changed, 45 insertions(+), 71 deletions(-) diff --git a/nfs4.0/lib/rpc/rpcsec/sec_auth_gss.py b/nfs4.0/lib/rpc/rpcsec/sec_auth_gss.py index 1b5eb93..6577fcf 100644 --- a/nfs4.0/lib/rpc/rpcsec/sec_auth_gss.py +++ b/nfs4.0/lib/rpc/rpcsec/sec_auth_gss.py @@ -1,8 +1,8 @@ from .base import SecFlavor, SecError from rpc.rpc_const import RPCSEC_GSS from rpc.rpc_type import opaque_auth -from gss_const import * -import gss_pack +from .gss_const import * +from . import gss_pack import gss_type import gssapi import threading @@ -125,50 +125,41 @@ class SecAuthGss(SecFlavor): def initialize(self, client): # Note this is not thread safe """Set seq_num, init, handle, and context""" self.gss_seq_num = 0 - d = gssapi.importName("nfs@%s" % client.remotehost) - if d['major'] != gssapi.GSS_S_COMPLETE: - raise SecError("gssapi.importName returned: %s" % \ - show_major(d['major'])) - name = d['name'] - # We need to send NULLPROCs with token from initSecContext - good_major = [gssapi.GSS_S_COMPLETE, gssapi.GSS_S_CONTINUE_NEEDED] + name = gssapi.Name("nfs@%s" % client.remotehost, gssapi.NameType.hostbased_service) + # We need to send NULLPROCs with token from SecurityContext + good_major = [GSS_S_COMPLETE, GSS_S_CONTINUE_NEEDED] self.init = 1 - reply_token = None - reply_major = '' - context = None + input_token = None + + # RFC2203 5.2.2. Context Creation Requests + # When GSS_Init_sec_context() is called, the parameters + # replay_det_req_flag and sequence_req_flag must be turned off. + + # Note - by default, out_of_sequence_detection flag (sequence_req_flag) is used by gssapi.init_sec_context() + # and we have 'An expected per-message token was not received' error (GSS_S_GAP_TOKEN). + # To prevent this, we need to use default flags without out_of_sequence_detection bit. + flags = gssapi.IntEnumFlagSet(gssapi.RequirementFlag, [gssapi.RequirementFlag.mutual_authentication]) + context = gssapi.SecurityContext(name=name, flags=flags) while True: - d = gssapi.initSecContext(name, context, reply_token) - major = d['major'] - context = d['context'] - if major not in good_major: - raise SecError("gssapi.initSecContext returned: %s" % \ - show_major(major)) - if (major == gssapi.GSS_S_CONTINUE_NEEDED) and \ - (reply_major == gssapi.GSS_S_COMPLETE): - raise SecError("Unexpected GSS_S_COMPLETE from server") - token = d['token'] - if reply_major != gssapi.GSS_S_COMPLETE: - # FRED - sec 5.2.2 of RFC 2203 mentions possibility that - # no token is returned. But then how get handle? - p = self.getpacker() - p.reset() - p.pack_opaque(token) - data = p.get_buffer() - reply = client.call(0, data) - up = self.getunpacker() - up.reset(reply) - res = up.unpack_rpc_gss_init_res() - up.done() - reply_major = res.gss_major - if reply_major not in good_major: - raise SecError("Server returned: %s" % \ - show_major(reply_major)) - self.init = 2 - reply_token = res.gss_token - if major == gssapi.GSS_S_COMPLETE: - if reply_major != gssapi.GSS_S_COMPLETE: - raise SecError("Unexpected COMPLETE from client") + # note - gssapi will raise an exception here automatically in case of failure + output_token = context.step(input_token) + if context.complete: break + p = self.getpacker() + p.reset() + p.pack_opaque(output_token) + data = p.get_buffer() + reply = client.call(0, data) + up = self.getunpacker() + up.reset(reply) + res = up.unpack_rpc_gss_init_res() + up.done() + reply_major = res.gss_major + if reply_major not in good_major: + raise SecError("Server returned: %s" % \ + show_major(reply_major)) + self.init = 2 + input_token = res.gss_token self.gss_context = context self.gss_handle = res.handle self.init = 0 @@ -176,7 +167,7 @@ class SecAuthGss(SecFlavor): def make_cred(self): """Credential sent with each RPC call""" if self.init == 1: # first call in context creation - cred = self._make_cred_gss('', rpc_gss_svc_none, RPCSEC_GSS_INIT) + cred = self._make_cred_gss(b'', rpc_gss_svc_none, RPCSEC_GSS_INIT) elif self.init > 1: # subsequent calls in context creation cred = self._make_cred_gss('', rpc_gss_svc_none, RPCSEC_GSS_CONTINUE_INIT) @@ -237,12 +228,8 @@ class SecAuthGss(SecFlavor): if self.init: return self._none else: - d = gssapi.getMIC(self.gss_context, data) - major = d['major'] - if major != gssapi.GSS_S_COMPLETE: - raise SecError("gssapi.getMIC returned: %s" % \ - show_major(major)) - return opaque_auth(RPCSEC_GSS, d['token']) + token = self.gss_context.get_signature(data) + return opaque_auth(RPCSEC_GSS, token) def _make_cred_gss(self, handle, service, gss_proc=RPCSEC_GSS_DATA, seq=0): data = gss_type.rpc_gss_cred_vers_1_t(gss_proc, seq, service, handle) @@ -264,13 +251,10 @@ class SecAuthGss(SecFlavor): p.reset() p.pack_uint(gss_cred.seq_num) data = p.get_buffer() + data - d = gssapi.getMIC(self.gss_context, data) - if d['major'] != gssapi.GSS_S_COMPLETE: - raise SecError("gssapi.getMIC returned: %s" % \ - show_major(d['major'])) + token = self.gss_context.get_signature(data) p.reset() p.pack_opaque(data) - p.pack_opaque(d['token']) + p.pack_opaque(token) data = p.get_buffer() elif gss_cred.service == rpc_gss_svc_privacy: # data = opaque[wrap([gss_seq_num+data])] @@ -278,12 +262,9 @@ class SecAuthGss(SecFlavor): p.reset() p.pack_uint(gss_cred.seq_num) data = p.get_buffer() + data - d = gssapi.wrap(self.gss_context, data) - if d['major'] != gssapi.GSS_S_COMPLETE: - raise SecError("gssapi.wrap returned: %s" % \ - show_major(d['major'])) + wrap_data = self.gss_context.wrap(data, encrypt=True) p.reset() - p.pack_opaque(d['msg']) + p.pack_opaque(wrap_data.message) data = p.get_buffer() else: # Not really necessary, should have already raised XDRError @@ -303,10 +284,7 @@ class SecAuthGss(SecFlavor): data = p.unpack_opaque() checksum = p.unpack_opaque() p.done() - d = gssapi.verifyMIC(self.gss_context, data, checksum) - if d['major'] != gssapi.GSS_S_COMPLETE: - raise SecError("gssapi.verifyMIC returned: %s" % \ - show_major(d['major'])) + qop = self.gss_context.verify_signature(data, checksum) p.reset(data) seqnum = p.unpack_uint() if seqnum != gss_cred.seq_num: @@ -320,11 +298,8 @@ class SecAuthGss(SecFlavor): p.reset(data) data = p.unpack_opaque() p.done() - d = gssapi.unwrap(self.gss_context, data) - if d['major'] != gssapi.GSS_S_COMPLETE: - raise SecError("gssapi.unwrap returned %s" % \ - show_major(d['major'])) - p.reset(d['msg']) + data, encrypted, qop = self.gss_context.unwrap(data) + p.reset(data) seqnum = p.unpack_uint() if seqnum != gss_cred.seq_num: raise SecError(\ @@ -373,8 +348,7 @@ class SecAuthGss(SecFlavor): p = self.getpacker() p.reset() p.pack_uint(cred.seq_num) - d = gssapi.verifyMIC(self.gss_context, p.get_buffer(), rverf.body) - #print("Verify(%i):"%cred.seq_num, show_major(d['major']), show_minor(d['minor'])) + qop = self.gss_context.verify_signature(p.get_buffer(), rverf.body) else: pass -- 2.24.1