[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
a gserver patch
From: |
Mei-Hui Su |
Subject: |
a gserver patch |
Date: |
Tue, 1 May 2001 09:20:48 -0700 (PDT) |
Hi,
As part of the Globus Project (www.globus.org), we
have developed a PKI based GSSAPI implementation called
the Grid Security infrastructure (GSI). As part of this
project, we are actively working within the IETF to
standardize this binding. We have been able to use this
GSS-API implementation with cyrus-sasl on both openldap
and iplanet servers to do user authentication.
We are interested in using GSI with cvs. With cvs's
gserver, the GSI should just plugin and work. But we
found that the implementation of the server's use of
the GSS-API is not completely complient with the
specification. Specifically, we found, both client.c
and server.c were not handling multiple server-client
handshaking properly. For example, client did not check
if it is expected to receive a token from server (a check
on stat_maj for GSS_S_CONTINUE_NEEDED) before attempting
to do recv_byte. Server did not have a do-loop to match
client's and hence can not handle more than one hand-shake.
In Kerberos, there is just one round-trip and so these
kind of problems did not show up.
We were able to make some minimal changes to cvs's
server and client code to use GSI instead of Kerberos.
These changes are backward compatible with the Kerberos
GSS-API use and make the cvs cleint/server code's use of
GSS-API more consistant with correct API usage.
There are just 4 files that were changed. configure.in,
configure, src/server.c and src/client.c. The only GSI
specific code is in src/server.c which uses GSI's gridmap
to do access control, by mapping names in X.509 certificates
to local user ids.
We would like you to consider incorporating these changes
into the CVS release. Ideally, one would like to consider
using SASL as the security API used by CVS, rather then GSS-API.
We have significant experience with SASL as well, and if it
would make it more likely to include our mods, would consider
providing the necessary verions of the client and server.
mei
(mei@isi.edu)
diff -u -r cvs/configure cvs-1.11/configure
--- cvs/configure Thu Apr 26 13:54:31 2001
+++ cvs-1.11/configure Tue Aug 1 09:13:25 2000
@@ -3232,7 +3232,8 @@
CPPFLAGS=$hold_cppflags
-if (test "$ac_cv_header_gssapi_h" = "yes" ||
+if test "$ac_cv_header_krb5_h" = "yes" &&
+ (test "$ac_cv_header_gssapi_h" = "yes" ||
test "$ac_cv_header_gssapi_gssapi_h" = "yes"); then
cat >> confdefs.h <<\EOF
#define HAVE_GSSAPI 1
@@ -3241,11 +3242,7 @@
includeopt="${includeopt} -I$GSSAPI/include"
# FIXME: This is ugly, but these things don't seem to be standardized.
if test "$ac_cv_header_gssapi_h" = "yes"; then
- if (test "$ac_cv_header_krb5_h" = "yes" &&
- test "$GSSAPI_LIBS+set" = "set"); then
- GSSAPI_LIB="-lgssapi -lkrb5 -lasn1 -ldes -lroken"
- fi
- LIBS="$LIBS -L$GSSAPI/lib $GSSAPI_LIBS"
+ LIBS="$LIBS -L$GSSAPI/lib -lgssapi -lkrb5 -lasn1 -ldes -lroken"
else
LIBS="$LIBS -L$GSSAPI/lib -lgssapi_krb5 -lkrb5 -lcrypto -lcom_err"
fi
@@ -3253,7 +3250,7 @@
CPPFLAGS="-I$GSSAPI/include $CPPFLAGS"
if test "$ac_cv_header_gssapi_h" = "yes"; then
cat > conftest.$ac_ext <<EOF
-#line 3257 "configure"
+#line 3254 "configure"
#include "confdefs.h"
#include <gssapi.h>
EOF
@@ -3269,7 +3266,7 @@
else
cat > conftest.$ac_ext <<EOF
-#line 3273 "configure"
+#line 3270 "configure"
#include "confdefs.h"
#include <gssapi/gssapi.h>
EOF
@@ -3288,7 +3285,7 @@
# This is necessary on Irix 5.3, in order to link against libkrb5 --
# there, an_to_ln.o refers to things defined only in -lgen.
echo $ac_n "checking for compile in -lgen""... $ac_c" 1>&6
-echo "configure:3292: checking for compile in -lgen" >&5
+echo "configure:3289: checking for compile in -lgen" >&5
ac_lib_var=`echo gen'_'compile | sed 'y%./+-%__p_%'`
if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
@@ -3296,7 +3293,7 @@
ac_save_LIBS="$LIBS"
LIBS="-lgen $LIBS"
cat > conftest.$ac_ext <<EOF
-#line 3300 "configure"
+#line 3297 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -3307,7 +3304,7 @@
compile()
; return 0; }
EOF
-if { (eval echo configure:3311: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } &&
test -s conftest${ac_exeext}; then
+if { (eval echo configure:3308: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } &&
test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_lib_$ac_lib_var=yes"
else
@@ -3356,12 +3353,12 @@
fi
echo $ac_n "checking for gethostname""... $ac_c" 1>&6
-echo "configure:3360: checking for gethostname" >&5
+echo "configure:3357: checking for gethostname" >&5
if eval "test \"`echo '$''{'ac_cv_func_gethostname'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
-#line 3365 "configure"
+#line 3362 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char gethostname(); below. */
@@ -3384,7 +3381,7 @@
; return 0; }
EOF
-if { (eval echo configure:3388: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } &&
test -s conftest${ac_exeext}; then
+if { (eval echo configure:3385: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } &&
test -s conftest${ac_exeext}; then
rm -rf conftest*
eval "ac_cv_func_gethostname=yes"
else
@@ -3449,14 +3446,14 @@
if test "$enable_server" = yes; then
echo $ac_n "checking for library containing crypt""... $ac_c" 1>&6
-echo "configure:3453: checking for library containing crypt" >&5
+echo "configure:3450: checking for library containing crypt" >&5
if eval "test \"`echo '$''{'ac_cv_search_crypt'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
ac_func_search_save_LIBS="$LIBS"
ac_cv_search_crypt="no"
cat > conftest.$ac_ext <<EOF
-#line 3460 "configure"
+#line 3457 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -3467,7 +3464,7 @@
crypt()
; return 0; }
EOF
-if { (eval echo configure:3471: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } &&
test -s conftest${ac_exeext}; then
+if { (eval echo configure:3468: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } &&
test -s conftest${ac_exeext}; then
rm -rf conftest*
ac_cv_search_crypt="none required"
else
@@ -3478,7 +3475,7 @@
test "$ac_cv_search_crypt" = "no" && for i in crypt; do
LIBS="-l$i $ac_func_search_save_LIBS"
cat > conftest.$ac_ext <<EOF
-#line 3482 "configure"
+#line 3479 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
@@ -3489,7 +3486,7 @@
crypt()
; return 0; }
EOF
-if { (eval echo configure:3493: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } &&
test -s conftest${ac_exeext}; then
+if { (eval echo configure:3490: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } &&
test -s conftest${ac_exeext}; then
rm -rf conftest*
ac_cv_search_crypt="-l$i"
break
@@ -3519,19 +3516,19 @@
echo $ac_n "checking for cygwin32""... $ac_c" 1>&6
-echo "configure:3523: checking for cygwin32" >&5
+echo "configure:3520: checking for cygwin32" >&5
if eval "test \"`echo '$''{'ccvs_cv_sys_cygwin32'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
-#line 3528 "configure"
+#line 3525 "configure"
#include "confdefs.h"
int main() {
return __CYGWIN32__;
; return 0; }
EOF
-if { (eval echo configure:3535: \"$ac_compile\") 1>&5; (eval $ac_compile)
2>&5; }; then
+if { (eval echo configure:3532: \"$ac_compile\") 1>&5; (eval $ac_compile)
2>&5; }; then
rm -rf conftest*
ccvs_cv_sys_cygwin32=yes
else
diff -u -r cvs/configure.in cvs-1.11/configure.in
--- cvs/configure.in Thu Apr 26 13:54:31 2001
+++ cvs-1.11/configure.in Tue Aug 1 09:13:25 2000
@@ -200,17 +200,14 @@
AC_CHECK_HEADERS(krb5.h gssapi.h gssapi/gssapi.h gssapi/gssapi_generic.h)
CPPFLAGS=$hold_cppflags
-if (test "$ac_cv_header_gssapi_h" = "yes" ||
+if test "$ac_cv_header_krb5_h" = "yes" &&
+ (test "$ac_cv_header_gssapi_h" = "yes" ||
test "$ac_cv_header_gssapi_gssapi_h" = "yes"); then
AC_DEFINE(HAVE_GSSAPI)
includeopt="${includeopt} -I$GSSAPI/include"
# FIXME: This is ugly, but these things don't seem to be standardized.
if test "$ac_cv_header_gssapi_h" = "yes"; then
- if (test "$ac_cv_header_krb5_h" = "yes" &&
- test "$GSSAPI_LIBS+set" = "set"); then
- GSSAPI_LIB="-lgssapi -lkrb5 -lasn1 -ldes -lroken"
- fi
- LIBS="$LIBS -L$GSSAPI/lib $GSSAPI_LIBS"
+ LIBS="$LIBS -L$GSSAPI/lib -lgssapi -lkrb5 -lasn1 -ldes -lroken"
else
LIBS="$LIBS -L$GSSAPI/lib -lgssapi_krb5 -lkrb5 -lcrypto -lcom_err"
fi
diff -u -r cvs/src/client.c cvs-1.11/src/client.c
--- cvs/src/client.c Fri Mar 23 11:08:18 2001
+++ cvs-1.11/src/client.c Thu Jul 6 09:20:41 2000
@@ -4104,7 +4104,7 @@
struct hostent *hostinfo;
{
char *str;
- char buf[4000];
+ char buf[1024];
gss_buffer_desc *tok_in_ptr, tok_in, tok_out;
OM_uint32 stat_min, stat_maj;
gss_name_t server_name;
@@ -4117,10 +4117,8 @@
sprintf (buf, "cvs@%s", hostinfo->h_name);
tok_in.length = strlen (buf);
tok_in.value = buf;
-
- if (gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE,
- &server_name) != GSS_S_COMPLETE)
- error (1, 0, "could not import GSSAPI service name %s", buf);
+ gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE,
+ &server_name);
tok_in_ptr = GSS_C_NO_BUFFER;
gcontext = GSS_C_NO_CONTEXT;
@@ -4134,7 +4132,6 @@
| GSS_C_REPLAY_FLAG),
0, NULL, tok_in_ptr, NULL, &tok_out,
NULL, NULL);
-
if (stat_maj != GSS_S_COMPLETE && stat_maj != GSS_S_CONTINUE_NEEDED)
{
OM_uint32 message_context;
@@ -4149,8 +4146,6 @@
message_context = 0;
gss_display_status (&new_stat_min, stat_min, GSS_C_MECH_CODE,
GSS_C_NULL_OID, &message_context, &tok_out);
- (void) gss_release_name(&stat_min, &server_name);
-
error (1, 0, "GSSAPI authentication failed: %s",
(char *) tok_out.value);
}
@@ -4166,58 +4161,44 @@
cbuf[0] = (tok_out.length >> 8) & 0xff;
cbuf[1] = tok_out.length & 0xff;
- if((send (sock, cbuf, 2, 0) < 0)
- || (send (sock, tok_out.value, tok_out.length, 0) < 0))
- {
- (void) gss_release_buffer(&stat_min, &tok_out);
- (void) gss_release_name(&stat_min, &server_name);
+ if (send (sock, cbuf, 2, 0) < 0)
+ error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
+ if (send (sock, tok_out.value, tok_out.length, 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
- }
- (void) gss_release_buffer(&stat_min, &tok_out);
+ recv_bytes (sock, cbuf, 2);
+ need = ((cbuf[0] & 0xff) << 8) | (cbuf[1] & 0xff);
- /* only if we still expecting a token from the server */
- if(stat_maj == GSS_S_CONTINUE_NEEDED ) {
- recv_bytes (sock, cbuf, 2);
- need = ((cbuf[0] & 0xff) << 8) | (cbuf[1] & 0xff);
-
- if (need > sizeof buf)
- {
- int got;
+ if (need > sizeof buf)
+ {
+ int got;
/* This usually means that the server sent us an error
message. Read it byte by byte and print it out.
FIXME: This is a terrible error handling strategy.
However, even if we fix the server, we will still
want to do this to work with older servers. */
- buf[0] = cbuf[0];
- buf[1] = cbuf[1];
- got = recv (sock, buf + 2, sizeof buf - 2, 0);
- if (got < 0)
- {
- error (1, 0, "recv() from server %s: %s",
- CVSroot_hostname, SOCK_STRERROR (SOCK_ERRNO));
- }
- buf[got + 2] = '\0';
- if (buf[got + 1] == '\n')
- buf[got + 1] = '\0';
-
- (void) gss_release_name(&stat_min, &server_name);
- error (1, 0, "error from server %s: %s", CVSroot_hostname,
- buf);
- }
-
- recv_bytes (sock, buf, need);
- tok_in.length = need;
+ buf[0] = cbuf[0];
+ buf[1] = cbuf[1];
+ got = recv (sock, buf + 2, sizeof buf - 2, 0);
+ if (got < 0)
+ error (1, 0, "recv() from server %s: %s",
+ CVSroot_hostname, SOCK_STRERROR (SOCK_ERRNO));
+ buf[got + 2] = '\0';
+ if (buf[got + 1] == '\n')
+ buf[got + 1] = '\0';
+ error (1, 0, "error from server %s: %s", CVSroot_hostname,
+ buf);
}
- tok_in.value = buf;
- tok_in_ptr = &tok_in;
- }
-
- } while (stat_maj == GSS_S_CONTINUE_NEEDED);
+ recv_bytes (sock, buf, need);
+ tok_in.length = need;
+ }
- (void) gss_release_name(&stat_min, &server_name);
+ tok_in.value = buf;
+ tok_in_ptr = &tok_in;
+ }
+ while (stat_maj == GSS_S_CONTINUE_NEEDED);
return 1;
}
diff -u -r cvs/src/server.c cvs-1.11/src/server.c
--- cvs/src/server.c Fri Apr 20 10:23:49 2001
+++ cvs-1.11/src/server.c Fri Jul 28 13:18:40 2000
@@ -40,6 +40,7 @@
#endif
#ifdef HAVE_GSSAPI
+
#include <netdb.h>
#ifdef HAVE_GSSAPI_H
@@ -56,17 +57,9 @@
#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
#endif
-#ifdef HAVE_KERBEROS
/* We use Kerberos 5 routines to map the GSSAPI credential to a user
name. */
#include <krb5.h>
-#endif
-
-#ifdef HAVE_GSI
-/* We use GSI's gridmap routines to map the GSSAPI credential to a user
- name. */
-#include <globus_gss_assist.h>
-#endif
/* We need this to wrap data. */
static gss_ctx_id_t gcontext;
@@ -192,48 +185,6 @@
static int fd_buffer_block PROTO((void *, int));
static int fd_buffer_shutdown PROTO((void *));
-#ifdef HAVE_GSI
-char *
-variable_get (name)
- char *name;
-{
- char *p; /* to point to the val */
- Node *node;
-
- if (variable_list == NULL)
- return NULL;
-
- node = findnode (variable_list, name);
- if (node != NULL)
- {
- return node->data;
- }
- return NULL;
-}
-
-void
-retrieve_gsi_conf()
-{
- char *conffile;
-
- conffile=variable_get("GSI_CONF_FILE");
-
- if(conffile) {
- char buf[1024];
- FILE *fp;
- fp=fopen(conffile,"r");
- if(fp) {
- while (fgets(buf, sizeof buf, fp) != NULL) {
- if(buf[strlen(buf)-1]=='\n') /* remove the newline */
- buf[strlen(buf)-1]='\0'; /* remove the newline */
- (void) putenv(xstrdup(buf));
- }
- fclose(fp);
- }
- }
-}
-#endif
-
/* Initialize a buffer built on a file descriptor. FD is the file
descriptor. INPUT is nonzero if this is for input, zero if this is
for output. MEMORY is the function to call when a memory error
@@ -5701,10 +5652,6 @@
{
#ifdef HAVE_GSSAPI
free (tmp);
-
-#ifdef HAVE_GSI
-retrieve_gsi_conf();
-#endif
gserver_authenticate_connection ();
return;
#else
@@ -5902,8 +5849,8 @@
char hostname[MAXHOSTNAMELEN];
struct hostent *hp;
gss_buffer_desc tok_in, tok_out;
- char buf[4000];
- OM_uint32 stat_min, stat_maj, ret;
+ char buf[1024];
+ OM_uint32 stat_min, ret;
gss_name_t server_name, client_name;
gss_cred_id_t server_creds;
int nbytes;
@@ -5927,31 +5874,26 @@
if (gss_acquire_cred (&stat_min, server_name, 0, GSS_C_NULL_OID_SET,
GSS_C_ACCEPT, &server_creds,
NULL, NULL) != GSS_S_COMPLETE)
- {
- (void) gss_release_name(&stat_min, &server_name);
error (1, 0, "could not acquire GSSAPI server credentials");
- }
gss_release_name (&stat_min, &server_name);
- gcontext = GSS_C_NO_CONTEXT;
-
- do {
/* The client will send us a two byte length followed by that many
bytes. */
- if (fread (buf, 1, 2, stdin) != 2)
- error (1, errno, "read of length failed");
+ if (fread (buf, 1, 2, stdin) != 2)
+ error (1, errno, "read of length failed");
- nbytes = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
- assert (nbytes <= sizeof buf);
+ nbytes = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
+ assert (nbytes <= sizeof buf);
- if (fread (buf, 1, nbytes, stdin) != nbytes)
- error (1, errno, "read of data failed");
+ if (fread (buf, 1, nbytes, stdin) != nbytes)
+ error (1, errno, "read of data failed");
- tok_in.length = nbytes;
- tok_in.value = buf;
+ gcontext = GSS_C_NO_CONTEXT;
+ tok_in.length = nbytes;
+ tok_in.value = buf;
- stat_maj = gss_accept_sec_context (&stat_min,
+ if (gss_accept_sec_context (&stat_min,
&gcontext, /* context_handle */
server_creds, /* verifier_cred_handle */
&tok_in, /* input_token */
@@ -5961,34 +5903,12 @@
&tok_out, /* output_token */
&ret,
NULL, /* ignore time_rec */
- NULL); /* ignore del_cred_handle */
- if(stat_maj != GSS_S_COMPLETE && stat_maj != GSS_S_CONTINUE_NEEDED)
- {
- error (1, 0, "could not verify credentials");
- }
-
- if (tok_out.length != 0)
- {
- char cbuf[2];
-
- cbuf[0] = (tok_out.length >> 8) & 0xff;
- cbuf[1] = tok_out.length & 0xff;
- if (fwrite (cbuf, 1, 2, stdout) != 2
- || (fwrite (tok_out.value, 1, tok_out.length, stdout)
- != tok_out.length))
- {
- (void) gss_release_buffer(&stat_min, &tok_out);
- error (1, errno, "fwrite failed");
- }
-
- (void) gss_release_buffer(&stat_min, &tok_out);
- }
-
- fflush(stdout);
-
- } while (stat_maj == GSS_S_CONTINUE_NEEDED);
+ NULL) /* ignore del_cred_handle */
+ != GSS_S_COMPLETE)
+ {
+ error (1, 0, "could not verify credentials");
+ }
-#ifdef HAVE_KERBEROS
/* FIXME: Use Kerberos v5 specific code to authenticate to a user.
We could instead use an authentication to access mapping. */
{
@@ -5999,51 +5919,27 @@
krb5_init_context (&kc);
if (gss_display_name (&stat_min, client_name, &desc,
&mechid) != GSS_S_COMPLETE
- || krb5_parse_name (kc, ((gss_buffer_t) &desc)->value, &p) != 0
- || krb5_aname_to_localname (kc, p, sizeof buf, buf) != 0
- || krb5_kuserok (kc, p, buf) != TRUE)
- {
+ || krb5_parse_name (kc, ((gss_buffer_t) &desc)->value, &p) != 0
+ || krb5_aname_to_localname (kc, p, sizeof buf, buf) != 0
+ || krb5_kuserok (kc, p, buf) != TRUE)
+ {
error (1, 0, "access denied");
}
- krb5_free_principal (kc, p);
- krb5_free_context (kc);
+ krb5_free_principal (kc, p);
+ krb5_free_context (kc);
}
-#endif
-#ifdef HAVE_GSI
-/* get the name from the last token */
- {
- gss_buffer_desc name_token ;
- char *authcid=NULL;
- char *ubuf=NULL;
- if (gss_display_name (&stat_min, client_name, &name_token,
- NULL) != GSS_S_COMPLETE)
- {
- error (1, 0, "access denied");
- }
- if(name_token.value)
- {
- authcid=strdup((char *)name_token.value);
- gss_release_buffer(&stat_min, &name_token);
- } else {
- error (1, 0, "access denied: name_token is NULL?");
- }
-
- if(authcid==NULL ||
- globus_gss_assist_gridmap(authcid, &ubuf)==1)
- {
- error (1, 0, "access denied: gridmap lookup failed");
- }
+ if (tok_out.length != 0)
+ {
+ char cbuf[2];
- if(authcid) free(authcid);
- if(ubuf)
- {
- strcpy(buf,ubuf);
- free(ubuf);
- }
+ cbuf[0] = (tok_out.length >> 8) & 0xff;
+ cbuf[1] = tok_out.length & 0xff;
+ if (fwrite (cbuf, 1, 2, stdout) != 2
+ || (fwrite (tok_out.value, 1, tok_out.length, stdout)
+ != tok_out.length))
+ error (1, errno, "fwrite failed");
}
-#endif
- (void) gss_release_name(&stat_min, &client_name);
switch_to_user (buf);
- a gserver patch,
Mei-Hui Su <=