Hi Guido,

On Thu, Jun 12, 2008 at 05:39:57PM +0200, Guido Günther wrote:
> On Thu, Jun 12, 2008 at 04:01:06PM +0200, Jelmer Vernooij wrote:
> > Hi Guido,
> > Am Donnerstag, den 12.06.2008, 09:51 +0200 schrieb Guido Günther:
> > > On Wed, Jun 11, 2008 at 11:14:52PM +0200, Jelmer Vernooij wrote:
> > > > How should I go about allowing more generic use? Would it be ok to
> > > > break the existing API? Should I add a new call? 
> > > I think the API can be extended without breaking it. The current call
> > > sets GSS_AUTH_P_NONE. If you want to setup real message integrity
> > > checking/encryption we can add these as parameters. 
> > > We can also skip the part you ripped out by just jumping over it in case
> > > we pass in a NULL/NONE username. 
> > Thanks, I'll have a look at doing that. 
> Looking at your code this should indeed work out. We can simply skip the
> code you ripped out in case user == NULL in
> authenticate_gss_client_wrap. I should have moved this part into a
> different function in the first place.
Makes sense; I've attached a patch that does this. Does this look ok?

> [..snip..] 
> > > I'd be interested to know what kind of respone buffer you pass in in
> > > that case.
> > I'm implementing RFC2228 (GSSAPI Authentication + Encryption for FTP).
> > The attached script extends ftplib.FTP to support GSSAPI logins and
> > provides a very simple command-line FTP client that supports GSSAPI
> > logins. It needs the patch I attached earlier. Feel free to include it
> > in pykerberos as example; I can provide it under a different license if
> > necessary.
> This would make a great example indeed!
Updated version attached as well, with license changed to apache
license.

Cheers,

Jelmer

##
# Copyright (c) 2008 Jelmer Vernooij <[EMAIL PROTECTED]>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Support for secure authentication using GSSAPI over FTP.

See RFC2228 for details.
"""

from ftplib import *

import base64, ftplib, getpass, kerberos, socket, sys


class SecureFtp(FTP):
    """Extended version of ftplib.FTP that can authenticate using GSSAPI."""
    def mic_putcmd(self, line):
        rc = kerberos.authGSSClientWrap(self.vc, base64.b64encode(line))
        wrapped = kerberos.authGSSClientResponse(self.vc)
        FTP.putcmd(self, "MIC " + wrapped)

    def mic_getline(self):
        resp = FTP.getline(self)
        assert resp[:4] == '631 '
        rc = kerberos.authGSSClientUnwrap(self.vc, resp[4:].strip("\r\n"))
        response = base64.b64decode(kerberos.authGSSClientResponse(self.vc))
        return response

    def gssapi_login(self, user):
        # Try GSSAPI login first
        resp = self.sendcmd('AUTH GSSAPI')
        if resp[:3] == '334':
            rc, self.vc = kerberos.authGSSClientInit("[EMAIL PROTECTED]" % self.host)

            if kerberos.authGSSClientStep(self.vc, "") != 1:
                while resp[:3] in ('334', '335'):
                    authdata = kerberos.authGSSClientResponse(self.vc)
                    resp = self.sendcmd('ADAT ' + authdata)
                    if resp[:9] in ('235 ADAT=', '335 ADAT='):
                        rc = kerberos.authGSSClientStep(self.vc, resp[9:])
                        assert ((resp[:3] == '235' and rc == 1) or 
                                (resp[:3] == '335' and rc == 0))
            print "Authenticated as %s" % kerberos.authGSSClientUserName(self.vc)

            # Monkey patch ftplib
            self.putcmd = self.mic_putcmd
            self.getline = self.mic_getline

            self.sendcmd('USER ' + user)
            return resp


def test():
    '''Test program.
    Usage: ftp [-d] [-u[user]] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...

    -d dir
    -l list
    -u user
    '''
    from getopt import getopt

    if len(sys.argv) < 2:
        print test.__doc__
        sys.exit(0)

    (opts, args) = getopt(sys.argv[1:], "d:u:r:")

    debugging = 0
    rcfile = None
    userid = None

    for (k, v) in opts:
        if k == "-d":
            debugging += 1
        elif k == "-u":
            userid = v
        elif k == "-r":
            rcfile = v

    host = args[0]
    ftp = SecureFtp(host)
    ftp.set_debuglevel(debugging)
    passwd = acct = ''
    try:
        netrc = Netrc(rcfile)
    except IOError:
        if rcfile is not None and userid is None:
            sys.stderr.write("Could not open account file"
                             " -- using anonymous login.")
            userid = ''
    else:
        if userid is None:
            try:
                userid, passwd, acct = netrc.get_account(host)
            except KeyError:
                # no account for host
                sys.stderr.write(
                        "No account -- using anonymous login.")
                userid = ''
    try:
        if userid:
            ftp.gssapi_login(userid)
        else:
            ftp.login(userid, passwd, acct)
    except ftplib.error_perm, e:
        # Fall back to regular authentication
        ftp.login(userid, passwd, acct)
    for file in args[1:]:
        if file[:2] == '-l':
            ftp.dir(file[2:])
        elif file[:2] == '-d':
            cmd = 'CWD'
            if file[2:]: cmd = cmd + ' ' + file[2:]
            resp = ftp.sendcmd(cmd)
        elif file == '-p':
            ftp.set_pasv(not ftp.passiveserver)
        else:
            ftp.retrbinary('RETR ' + file, \
                           sys.stdout.write, 1024)
    ftp.quit()


if __name__ == '__main__':
    test()
=== modified file 'pysrc/kerberos.py'
--- pysrc/kerberos.py	2008-05-23 16:41:18 +0000
+++ pysrc/kerberos.py	2008-06-12 20:58:46 +0000
@@ -146,12 +146,12 @@
     @return: a result code (see above) 
     """ 
 
-def authGSSClientWrap(context, data, user): 
+def authGSSClientWrap(context, data, user=None): 
     """ 
     Perform the client side GSSAPI wrap step.  
     
     @param data:the result of the authGSSClientResponse after the authGSSClientUnwrap 
-    @param user: the user to authorize 
+    @param user: the user to authorize (optional)
     @return: a result code (see above) 
     """ 
 

=== modified file 'src/kerberos.c'
--- src/kerberos.c	2008-05-23 16:40:38 +0000
+++ src/kerberos.c	2008-06-12 20:59:04 +0000
@@ -201,19 +201,25 @@
 {
 	gss_client_state *state;
 	PyObject *pystate;
-	char *challenge, *user;
+	char *challenge, *user = NULL;
 	int result = 0;
 
-	if (!PyArg_ParseTuple(args, "Oss", &pystate, &challenge, &user) || !PyCObject_Check(pystate))
+	if (!PyArg_ParseTuple(args, "Os|z", &pystate, &challenge, &user) || !PyCObject_Check(pystate))
 		return NULL;
 
 	state = (gss_client_state *)PyCObject_AsVoidPtr(pystate);
 	if (state == NULL)
 		return NULL;
 
-	result = authenticate_gss_client_wrap(state, challenge, user);
-	if (result == AUTH_GSS_ERROR)
-		return NULL;
+	if (user == NULL) {
+		result = authenticate_gss_client_wrap(state, challenge);
+		if (result == AUTH_GSS_ERROR)
+			return NULL;
+	} else {
+		result = authenticate_gss_client_wrap_username(state, challenge, user);
+		if (result == AUTH_GSS_ERROR)
+			return NULL;
+	}
 
 	return Py_BuildValue("i", result);
 }

=== modified file 'src/kerberosgss.c'
--- src/kerberosgss.c	2008-05-23 16:40:38 +0000
+++ src/kerberosgss.c	2008-06-12 20:53:54 +0000
@@ -309,7 +309,58 @@
 	return ret;
 }
 
-int authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user)
+int authenticate_gss_client_wrap(gss_client_state* state, const char* data)
+{
+	OM_uint32 maj_stat;
+	OM_uint32 min_stat;
+	gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+	gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+	int ret = AUTH_GSS_CONTINUE;
+
+	// Always clear out the old response
+	if (state->response != NULL)
+	{
+		free(state->response);
+		state->response = NULL;
+	}
+
+	if (data && *data)
+	{
+		int len;
+		input_token.value = base64_decode(data, &len);
+		input_token.length = len;
+	}
+
+	// Do GSSAPI wrap
+	maj_stat = gss_wrap(&min_stat,
+						state->context,
+						0,
+						GSS_C_QOP_DEFAULT,
+						&input_token,
+						NULL,
+						&output_token);
+
+	if (maj_stat != GSS_S_COMPLETE)
+	{
+		set_gss_error(maj_stat, min_stat);
+		ret = AUTH_GSS_ERROR;
+		goto end;
+	}
+	else
+		ret = AUTH_GSS_COMPLETE;
+	// Grab the client response to send back to the server
+	if (output_token.length)
+	{
+		state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);;
+		maj_stat = gss_release_buffer(&min_stat, &output_token);
+	}
+end:
+	if (output_token.value)
+		gss_release_buffer(&min_stat, &output_token);
+	return ret;
+}
+
+int authenticate_gss_client_wrap_username(gss_client_state* state, const char* challenge, const char* user)
 {
 	OM_uint32 maj_stat;
 	OM_uint32 min_stat;
=== modified file 'src/kerberosgss.h'
--- src/kerberosgss.h	2008-05-23 16:40:38 +0000
+++ src/kerberosgss.h	2008-06-12 20:51:42 +0000
@@ -53,7 +53,8 @@
 int authenticate_gss_client_clean(gss_client_state *state);
 int authenticate_gss_client_step(gss_client_state *state, const char *challenge);
 int authenticate_gss_client_unwrap(gss_client_state* state, const char* challenge); 
-int authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user);
+int authenticate_gss_client_wrap(gss_client_state* state, const char* challenge);
+int authenticate_gss_client_wrap_username(gss_client_state* state, const char* challenge, const char* user);
 
 int authenticate_gss_server_init(const char* service, gss_server_state* state);
 int authenticate_gss_server_clean(gss_server_state *state);

_______________________________________________
calendarserver-dev mailing list
[email protected]
http://lists.macosforge.org/mailman/listinfo.cgi/calendarserver-dev

Reply via email to