Received: by 2002:a05:6a10:d5a5:0:0:0:0 with SMTP id gn37csp670452pxb; Thu, 30 Sep 2021 14:36:42 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxO1IGUDWZK+dSwCs4+6z0RXd4nEeiM23AIolJ+mZzsz9nsKZdTcjNoBDtd+BEpDmCD16+m X-Received: by 2002:a50:9d0e:: with SMTP id v14mr10267515ede.230.1633037802327; Thu, 30 Sep 2021 14:36:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1633037802; cv=none; d=google.com; s=arc-20160816; b=St0SY2+Hu/Cs1webhspguvXuxSptQqr9Ccr62eXioP4FgQTE/LeQk1Lbx0zRTJ/pM9 7g9GoxtRqNDPxkX9bbEsEGh40VzaMaTrGXVroBhLf0VW/RSiPBtIvJ73dLB7bHCWuHFc rbjrIOGLvZB3dHxjEkv7z/d4uRPidAnu9H7sReRboQrsUxCuFub9+fDknbjHqOMP/Wt/ xluwOKGKLrjvwOi2I4H0Kp52fsJesgH/vSu9i7cjQ4/UfjD0HIWnBnwcq9ibmLYmpA01 r15kFvJmow4SSkQZcHe8r53vBqBqLaviYGsSpphUOeqTyoLO0ed3ffI1u7JExUeF7ByT yuiQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:user-agent:in-reply-to:content-disposition :mime-version:references:message-id:subject:cc:to:from:date :dkim-signature:dkim-filter; bh=p+uZ/D2BSWs5MAF5u7x1k9C/K4QvusjfM8gWo2rbyKg=; b=l4YZ4Psd4PcYOHlaxpkoHl48mQs/ne3YCElWS2z6hfDNR562KFgg8P58bVi5N7Mf/g AFwurr51RHC85UzMEiTmaK22jYT1mS+wkadNkwUsfqRY0kim3TJZb6IsGaCGRGBZJvu4 u8K0KIwXjlHjCAID7wCTfmMMO3nMstHqkMSUuj8nn7aoOvzR7dy75AzLFqmttGHDt0Za IcwiyB8aS7nGxDjMWpJjikAdtuCwAWvUN6o1GwSQCTXX9ouhwQEtL35O3EoNOYeY32vd cs4LQ8e9x+ul12YSE52dqzAJzzC+IjKz4FmU0H+Lq84VLJC4BhJRbaJbVWs2WIzvEYAt 1ahQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@fieldses.org header.s=default header.b=F+euVjrr; spf=pass (google.com: domain of linux-nfs-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-nfs-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id v13si776219edr.596.2021.09.30.14.36.18; Thu, 30 Sep 2021 14:36:42 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-nfs-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@fieldses.org header.s=default header.b=F+euVjrr; spf=pass (google.com: domain of linux-nfs-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-nfs-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1348777AbhI3VNJ (ORCPT + 99 others); Thu, 30 Sep 2021 17:13:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44668 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1348779AbhI3VNG (ORCPT ); Thu, 30 Sep 2021 17:13:06 -0400 Received: from fieldses.org (fieldses.org [IPv6:2600:3c00:e000:2f7::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D9E22C06176A for ; Thu, 30 Sep 2021 14:11:23 -0700 (PDT) Received: by fieldses.org (Postfix, from userid 2815) id 3108C703F; Thu, 30 Sep 2021 17:11:23 -0400 (EDT) DKIM-Filter: OpenDKIM Filter v2.11.0 fieldses.org 3108C703F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fieldses.org; s=default; t=1633036283; bh=p+uZ/D2BSWs5MAF5u7x1k9C/K4QvusjfM8gWo2rbyKg=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=F+euVjrrkaSJJkMrlQLXmlkfzaFtIyiq2yPJkIQlshF8V0D9MV7HmhxSBLtC+QdFw Hk7c5tE1w9j6/LVjFfwn/HQCdK0VplF2193vT6zqj6nRS2AokGLn9IPy8uP3SWDCw7 N6gGGfRrCQ6+Td5b9DB5WMJyuG6u5SjzxWK2jh+c= Date: Thu, 30 Sep 2021 17:11:23 -0400 From: "J. Bruce Fields" To: Volodymyr Khomenko Cc: linux-nfs@vger.kernel.org Subject: Re: GSSAPI fix for pynfs nfs4.1 client code Message-ID: <20210930211123.GA16927@fieldses.org> References: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.21 (2010-09-15) Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org On Thu, Sep 30, 2021 at 06:22:09PM +0300, Volodymyr Khomenko wrote: > commit b77dc49c775756f08bdd0c6ebbe67a96f0ffe41f > Author: Volodymyr Khomenko > Date: Thu Sep 30 17:53:04 2021 +0300 > > Fixed GSSContext to start sequence numbering from 1 > > GSS sequence number 0 is usually used by NFS4 NULL request > during GSS context establishment (but ignored by server). > Client should never reuse GSS sequence number, so using > 0 for the next real operation (EXCHANGE_ID) is possible but > looks suspicious. Fixed the code so numbering for operations > is done from 1 to avoid confusion. So, I can verify that --security=krb5 works after this patch but not before, good. But why is that? As you say, the server is supposed to ignore the sequence number on context creation requests. And 0 is valid sequence number as far as I know. --b. > > Signed-off-by: Volodymyr Khomenko > > diff --git a/rpc/security.py b/rpc/security.py > index 0682f43..86f6592 100644 > --- a/rpc/security.py > +++ b/rpc/security.py > @@ -174,7 +174,9 @@ class GSSContext(object): > def __init__(self, context_ptr): > self.lock = threading.Lock() > self.ptr = context_ptr > - self.seqid = 0 # client - next seqid to use > + # Note - seqid=0 is usually used during GSS context establishment, > + # to have the unique number we need to use the next value now. > + self.seqid = 1 # client - next seqid to use > self.highest = 0 # server - highest seqid seen > self.seen = 0 # server - bitmask of seen requests > > commit a612cf9897f0fa5b5de94885e00ef9293e93ffa3 > Author: Volodymyr Khomenko > Date: Thu Sep 30 16:29:07 2021 +0300 > > Fixed gssapi usage (RPCGSS) for nfs4.1 client > > 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: > ./nfs4.1/testserver.py server.fqdn:/export --maketree --security=krb5 all > > Signed-off-by: Volodymyr Khomenko > > diff --git a/rpc/security.py b/rpc/security.py > index fe4390c..0682f43 100644 > --- a/rpc/security.py > +++ b/rpc/security.py > @@ -10,6 +10,7 @@ from . import gss_type > from .gss_type import rpc_gss_init_res > try: > import gssapi > + from gssapi.raw.misc import GSSError > except ImportError: > print("Could not find gssapi module, proceeding without") > gssapi = None > @@ -242,11 +243,11 @@ class AuthGss(AuthNone): > > def init_cred(self, call, target="nfs@jupiter", source=None, oid=None): > # STUB - need intelligent way to set defaults > - good_major = [gssapi.GSS_S_COMPLETE, gssapi.GSS_S_CONTINUE_NEEDED] > + good_major = [GSS_S_COMPLETE, GSS_S_CONTINUE_NEEDED] > p = Packer() > up = GSSUnpacker('') > # Set target (of form nfs@SERVER) > - target = gssapi.Name(target, gssapi.NT_HOSTBASED_SERVICE) > + target = gssapi.Name(target, gssapi.NameType.hostbased_service) > # Set source (of form USERNAME) > if source is not None: > source = gssapi.Name(source, gssapi.NT_USER_NAME) > @@ -254,18 +255,26 @@ class AuthGss(AuthNone): > else: > # Just use default cred > gss_cred = None > - context = gssapi.Context() > - token = None > - handle = '' > + # 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=target, creds=gss_cred, flags=flags) > + input_token = None > + handle = b'' > proc = RPCSEC_GSS_INIT > - while True: > + while not context.complete: > # Call initSecContext. If it returns COMPLETE, we are done. > # If it returns CONTINUE_NEEDED, we must send d['token'] > # to the target, which will run it through acceptSecContext, > # and give us back a token we need to send through initSecContext. > # Repeat as necessary. > - token = context.init(target, token, gss_cred) > - if context.open: > + output_token = context.step(input_token) > + if context.complete: > # XXX if res.major == CONTINUE there is a bug in library code > # STUB - now what? Just use context? > # XXX need to use res.seq_window > @@ -277,16 +286,16 @@ class AuthGss(AuthNone): > gss_proc=proc) > proc = RPCSEC_GSS_CONTINUE_INIT > p.reset() > - p.pack_opaque(token) > + p.pack_opaque(output_token) > header, reply = call(p.get_buffer(), credinfo) > up.reset(reply) > res = up.unpack_rpc_gss_init_res() > up.done() > # res now holds relevent output from target's acceptSecContext call > if res.gss_major not in good_major: > - raise gssapi.Error(res.gss_major, res.gss_minor) > + raise GSSError(res.gss_major, res.gss_minor) > handle = res.handle # Should not change between calls > - token = res.gss_token # This needs to be sent to initSecContext > + input_token = res.gss_token # This needs to be sent to SecurityContext.step() > return CredInfo(self, context=handle) > > @staticmethod > @@ -361,7 +370,7 @@ class AuthGss(AuthNone): > except: > log_gss.exception("unsecure_data - initial unpacking") > raise rpclib.RPCUnsuccessfulReply(GARBAGE_ARGS) > - qop = context.verifyMIC(data, checksum) > + qop = context.verify_signature(data, checksum) > check_gssapi(qop) > data = pull_seqnum(data) > elif cred.service == rpc_gss_svc_privacy: > @@ -373,14 +382,14 @@ class AuthGss(AuthNone): > log_gss.exception("unsecure_data - initial unpacking") > raise rpclib.RPCUnsuccessfulReply(GARBAGE_ARGS) > # data, qop, conf = context.unwrap(data) > - data, qop = context.unwrap(data) > + data, encrypted, qop = context.unwrap(data) > check_gssapi(qop) > data = pull_seqnum(data) > else: > # Can't get here, but doesn't hurt > log_gss.error("Unknown service %i for RPCSEC_GSS" % cred.service) > - except gssapi.Error as e: > - log_gss.warn("unsecure_data: gssapi call returned %s" % e.name) > + except GSSError as e: > + log_gss.warn("unsecure_data: gssapi call returned %s" % str(e)) > raise rpclib.RPCUnsuccessfulReply(GARBAGE_ARGS) > return data > > @@ -397,7 +406,7 @@ class AuthGss(AuthNone): > # data = opaque[gss_seq_num+data] + opaque[checksum] > p.pack_uint(cred.seq_num) > data = p.get_buffer() + data > - token = context.getMIC(data) # XXX BUG set qop > + token = context.get_signature(data) # XXX BUG set qop > p.reset() > p.pack_opaque(data) > p.pack_opaque(token) > @@ -406,16 +415,16 @@ class AuthGss(AuthNone): > # data = opaque[wrap([gss_seq_num+data])] > p.pack_uint(cred.seq_num) > data = p.get_buffer() + data > - token = context.wrap(data) # XXX BUG set qop > + wrap_res = context.wrap(data, encrypt=True) # XXX BUG set qop > p.reset() > - p.pack_opaque(token) > + p.pack_opaque(wrap_res.message) > data = p.get_buffer() > else: > # Can't get here, but doesn't hurt > log_gss.error("Unknown service %i for RPCSEC_GSS" % cred.service) > - except gssapi.Error as e: > + except GSSError as e: > # XXX What now? > - log_gss.warn("secure_data: gssapi call returned %s" % e.name) > + log_gss.warn("secure_data: gssapi call returned %s" % str(e)) > raise > return data > > @@ -436,8 +445,8 @@ class AuthGss(AuthNone): > return rpclib.NULL_CRED > else: > data = self.partially_packed_header(xid, body) > - # XXX how handle gssapi.Error? > - token = self._get_context(body.cred.body.handle).getMIC(data) > + # XXX how handle GSSError? > + token = self._get_context(body.cred.body.handle).get_signature(data) > return opaque_auth(RPCSEC_GSS, token) > > def check_call_verf(self, xid, body): > @@ -448,10 +457,10 @@ class AuthGss(AuthNone): > return False > data = self.partially_packed_header(xid, body) > try: > - qop = self._get_context(body.cred.body.handle).verifyMIC(data, body.verf.body) > - except gssapi.Error as e: > + qop = self._get_context(body.cred.body.handle).verify_signature(data, body.verf.body) > + except GSSError as e: > log_gss.warn("Verifier checksum failed verification with %s" % > - e.name) > + str(e)) > return False > body.cred.body.qop = qop # XXX Where store this? > log_gss.debug("verifier checks out (qop=%i)" % qop) > @@ -522,10 +531,10 @@ class AuthGss(AuthNone): > context = self._get_context(cred.body.handle) > try: > token = context.accept(token) > - except gssapi.Error as e: > + except GSSError as e: > log_gss.debug("RPCSEC_GSS_INIT failed (%s, %i)!" % > - (e.name, e.minor)) > - res = rpc_gss_init_res('', e.major, e.minor, 0, '') > + (str(e), e.min_code)) > + res = rpc_gss_init_res('', e.maj_code, e.min_code, 0, '') > else: > log_gss.debug("RPCSEC_GSS_*INIT succeeded!") > if first: > @@ -538,9 +547,9 @@ class AuthGss(AuthNone): > else: > handle = cred.body.handle > if context.open: > - major = gssapi.GSS_S_COMPLETE > + major = GSS_S_COMPLETE > else: > - major = gssapi.GSS_S_CONTINUE_NEEDED > + major = GSS_S_CONTINUE_NEEDED > res = rpc_gss_init_res(handle, major, 0, # XXX can't see minor > WINDOWSIZE, token) > # Prepare response > @@ -559,15 +568,15 @@ class AuthGss(AuthNone): > # NOTE this relies on GSS_S_COMPLETE == rpc.SUCCESS == 0 > return rpclib.NULL_CRED > elif cred.gss_proc in (RPCSEC_GSS_INIT, RPCSEC_GSS_CONTINUE_INIT): > - # init requires getMIC(seq_window) > + # init requires get_signature(seq_window) > i = WINDOWSIZE > else: > - # Else return getMIC(cred.seq_num) > + # Else return get_signature(cred.seq_num) > i = cred.seq_num > p = Packer() > p.pack_uint(i) > # XXX BUG - need to set qop > - token = self._get_context(cred.handle).getMIC(p.get_buffer()) > + token = self._get_context(cred.handle).get_signature(p.get_buffer()) > return opaque_auth(RPCSEC_GSS, token) > > def check_reply_verf(self, msg, call_cred, data): > @@ -593,12 +602,12 @@ class AuthGss(AuthNone): > if res.gss_major != GSS_S_COMPLETE: > raise SecError("Expected NULL") > # BUG - context establishment is not finished on client > - # - so how get context? How run verifyMIC? > + # - so how get context? How run verify_signature? > # - This seems to be a protocol problem. Just ignore for now > else: > p = Packer() > p.pack_uint(call_cred.body.seq_num) > - qop = call_cred.context.verifyMIC(p.get_buffer(), verf.body) > + qop = call_cred.context.verify_signature(p.get_buffer(), verf.body) > if qop != call_cred.body.qop: > raise SecError("Mismatched qop") >