Index: configure =================================================================== RCS file: /cvsroot/cvs/ccvs/configure,v retrieving revision 1.420 diff -u -u -r1.420 configure --- configure 19 Sep 2005 21:29:26 -0000 1.420 +++ configure 21 Sep 2005 07:31:55 -0000 @@ -38307,11 +38307,10 @@ fi unset ac_cv_header_gssapi_h unset ac_cv_header_gssapi_gssapi_h - unset ac_cv_header_krb5_h -for ac_header in gssapi.h gssapi/gssapi.h krb5.h +for ac_header in gssapi.h gssapi/gssapi.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then @@ -38460,9 +38459,8 @@ done - if (test "$ac_cv_header_gssapi_h" = yes || - test "$ac_cv_header_gssapi_gssapi_h" = yes) && - test "$ac_cv_header_krb5_h" = yes; then + if test "$ac_cv_header_gssapi_h" = yes || + test "$ac_cv_header_gssapi_gssapi_h" = yes; then break fi done @@ -38508,7 +38506,7 @@ -for ac_header in gssapi.h gssapi/gssapi.h gssapi/gssapi_generic.h krb5.h +for ac_header in gssapi.h gssapi/gssapi.h gssapi/gssapi_generic.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then @@ -40014,6 +40012,8 @@ # # gssapi Heimdal K 0.3d -lkrb5 # + # gss Solaris 9 + # echo "$as_me:$LINENO: checking for library containing gss_import_name" >&5 echo $ECHO_N "checking for library containing gss_import_name... $ECHO_C" >&6 if test "${ac_cv_search_gss_import_name+set}" = set; then @@ -40074,7 +40074,7 @@ rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "$ac_cv_search_gss_import_name" = no; then - for ac_lib in gssapi_krb5 gssapi; do + for ac_lib in gssapi_krb5 gssapi gss; do LIBS="-l$ac_lib $ac_func_search_save_LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ Index: m4/acx_with_gssapi.m4 =================================================================== RCS file: /cvsroot/cvs/ccvs/m4/acx_with_gssapi.m4,v retrieving revision 1.1 diff -u -u -r1.1 acx_with_gssapi.m4 --- m4/acx_with_gssapi.m4 19 May 2003 19:43:15 -0000 1.1 +++ m4/acx_with_gssapi.m4 21 Sep 2005 07:31:56 -0000 @@ -79,11 +79,9 @@ fi unset ac_cv_header_gssapi_h unset ac_cv_header_gssapi_gssapi_h - unset ac_cv_header_krb5_h - AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h krb5.h]) - if (test "$ac_cv_header_gssapi_h" = yes || - test "$ac_cv_header_gssapi_gssapi_h" = yes) && - test "$ac_cv_header_krb5_h" = yes; then + AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h]) + if test "$ac_cv_header_gssapi_h" = yes || + test "$ac_cv_header_gssapi_gssapi_h" = yes; then break fi done @@ -121,7 +119,7 @@ # locate any other headers dnl We don't use HAVE_KRB5_H anywhere, but including it here might make it dnl easier to spot errors by reading configure output - AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h gssapi/gssapi_generic.h krb5.h]) + AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h gssapi/gssapi_generic.h]) # And look through them for GSS_C_NT_HOSTBASED_SERVICE or its alternatives AC_CACHE_CHECK( [for GSS_C_NT_HOSTBASED_SERVICE], @@ -276,6 +274,8 @@ # # gssapi Heimdal K 0.3d -lkrb5 # - AC_SEARCH_LIBS([gss_import_name], [gssapi_krb5 gssapi]) + # gss Solaris 9 + # + AC_SEARCH_LIBS([gss_import_name], [gssapi_krb5 gssapi gss]) fi ])dnl Index: src/buffer.c =================================================================== RCS file: /cvsroot/cvs/ccvs/src/buffer.c,v retrieving revision 1.61 diff -u -u -r1.61 buffer.c --- src/buffer.c 2 Sep 2005 21:51:08 -0000 1.61 +++ src/buffer.c 21 Sep 2005 07:31:58 -0000 @@ -1377,10 +1377,13 @@ /* Input data from a packetizing buffer. */ static int -packetizing_buffer_input (void *closure, char *data, size_t need, size_t size, +packetizing_buffer_input (void *closure, char *data, size_t aneed, size_t size, size_t *got) { struct packetizing_buffer *pb = closure; + ssize_t need = aneed; + + assert (aneed <= size); *got = 0; Index: src/gssapi-client.c =================================================================== RCS file: /cvsroot/cvs/ccvs/src/gssapi-client.c,v retrieving revision 1.9 diff -u -u -r1.9 gssapi-client.c --- src/gssapi-client.c 4 Nov 2004 21:22:27 -0000 1.9 +++ src/gssapi-client.c 21 Sep 2005 07:31:58 -0000 @@ -75,22 +75,28 @@ * Besides, I added some cruft to reenable the socket which shouldn't be * there. This would also enable its removal. */ -#define BUFSIZE 1024 int connect_to_gserver (cvsroot_t *root, int sock, struct hostent *hostinfo) { char *str; - char buf[BUFSIZE]; + char buf[1024]; gss_buffer_desc *tok_in_ptr, tok_in, tok_out; + gss_buffer_desc gss_status, mech_status; OM_uint32 stat_min, stat_maj; + OM_uint32 new_stat_min; gss_name_t server_name; + OM_uint32 message_context; + gss_OID actual_mech; + OM_uint32 actual_services; + char cbuf[2]; + int need; str = "BEGIN GSSAPI REQUEST\012"; if (send (sock, str, strlen (str), 0) < 0) error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO)); - if (strlen (hostinfo->h_name) > BUFSIZE - 5) + if (strlen (hostinfo->h_name) > sizeof(buf) - 5) error (1, 0, "Internal error: hostname exceeds length of buffer"); sprintf (buf, "cvs@%s", hostinfo->h_name); tok_in.length = strlen (buf); @@ -103,50 +109,82 @@ do { + /* + * Our GSS-API server compatible only with Kerberos. + * + * We made only two call gss_init_sec_context() per + * connection and one call gss_display_status() per + * messages. + * + * TODO: Strict conformity to GSS-API, see example + * . Need SPKI or + * other non Kerberos GSS-API implementation. + */ stat_maj = gss_init_sec_context (&stat_min, GSS_C_NO_CREDENTIAL, &gcontext, server_name, GSS_C_NULL_OID, (GSS_C_MUTUAL_FLAG - | 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; - OM_uint32 new_stat_min; - - message_context = 0; - gss_display_status (&new_stat_min, stat_maj, GSS_C_GSS_CODE, - GSS_C_NULL_OID, &message_context, &tok_out); - error (0, 0, "GSSAPI authentication failed: %s", - (const char *) tok_out.value); - - message_context = 0; - gss_display_status (&new_stat_min, stat_min, GSS_C_MECH_CODE, - GSS_C_NULL_OID, &message_context, &tok_out); - error (1, 0, "GSSAPI authentication failed: %s", - (const char *) tok_out.value); - } + | (cvsauthenticate + ? GSS_C_INTEG_FLAG|GSS_C_REPLAY_FLAG + : 0) +# ifdef ENCRYPTION + | (cvsencrypt + ? GSS_C_CONF_FLAG + : 0) +# endif /* ENCRYPTION */ + ), + 0, GSS_C_NO_CHANNEL_BINDINGS, + tok_in_ptr, &actual_mech, &tok_out, + &actual_services, NULL); + message_context = 0; + gss_display_status (&new_stat_min, stat_maj, GSS_C_GSS_CODE, + actual_mech, &message_context, &gss_status); + message_context = 0; + gss_display_status (&new_stat_min, stat_min, GSS_C_MECH_CODE, + actual_mech, &message_context, &mech_status); + + TRACE (TRACE_FUNCTION, "connect_to_gserver:%d:%s/%s", + __LINE__, (const char *) gss_status.value, + (const char *) mech_status.value); - if (tok_out.length == 0) + if (GSS_ERROR(stat_maj)) { - tok_in.length = 0; +# ifdef __sun + /* + * Default Solaris configuration - use diffie_hellman_640_0 + * Probably common mistake + */ + error (0, 0, "Check /etc/gss/mech; man mech"); +# endif /* __sun */ + error (1, 0, "GSSAPI authentication failed: %s/%s", + (const char *) gss_status.value, + (const char *) mech_status.value); } - else - { - char cbuf[2]; - int need; + gss_release_buffer(&new_stat_min, &gss_status); + gss_release_buffer(&new_stat_min, &mech_status); + + if (tok_out.length) + { cbuf[0] = (tok_out.length >> 8) & 0xff; cbuf[1] = tok_out.length & 0xff; 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)); + TRACE (TRACE_FUNCTION, "connect_to_gserver:%d: send %d", + __LINE__, tok_out.length); + gss_release_buffer(&stat_min, &tok_out); + } + if (stat_maj & GSS_S_CONTINUE_NEEDED) + { recv_bytes (sock, cbuf, 2); need = ((cbuf[0] & 0xff) << 8) | (cbuf[1] & 0xff); + TRACE (TRACE_FUNCTION, "connect_to_gserver:%d: recive %d", + __LINE__, need); + if (need > sizeof buf) { ssize_t got; @@ -178,13 +216,42 @@ recv_bytes (sock, buf, need); tok_in.length = need; + tok_in.value = buf; + tok_in_ptr = &tok_in; } + } + while (stat_maj&GSS_S_CONTINUE_NEEDED); - tok_in.value = buf; - tok_in_ptr = &tok_in; + TRACE (TRACE_FUNCTION, "connect_to_gserver:%d:%s%s%s%s", + __LINE__, + (actual_services&GSS_C_MUTUAL_FLAG) ? " GSS_C_MUTUAL_FLAG" : "", + (actual_services&GSS_C_REPLAY_FLAG) ? " GSS_C_REPLAY_FLAG" : "", + (actual_services&GSS_C_INTEG_FLAG) ? " GSS_C_INTEG_FLAG" : "", + (actual_services&GSS_C_CONF_FLAG) ? " GSS_C_CONF_FLAG" : "" + ); + +# ifdef ENCRYPTION + if (cvsencrypt && GSS_C_CONF_FLAG != (actual_services&GSS_C_CONF_FLAG)) + { + error(1, 0, "GSSAPI: traffic encryption don't agreement"); + return 0; } - while (stat_maj == GSS_S_CONTINUE_NEEDED); +# endif /* ENCRYPTION */ + if (cvsauthenticate && + (GSS_C_REPLAY_FLAG|GSS_C_INTEG_FLAG) != + (actual_services&(GSS_C_REPLAY_FLAG|GSS_C_INTEG_FLAG))) + { + error(1, 0, "GSSAPI: traffic authentication don't agreement"); + return 0; + } + if (GSS_C_MUTUAL_FLAG != (actual_services&GSS_C_MUTUAL_FLAG)) + { + error(1, 0, "GSSAPI: mutal authentication don't agreement"); + return 0; + } + TRACE (TRACE_FUNCTION, "connect_to_gserver:%d:gcontext=0x%x", + __LINE__, gcontext); return 1; } #endif /* CLIENT_SUPPORT */ Index: src/server.c =================================================================== RCS file: /cvsroot/cvs/ccvs/src/server.c,v retrieving revision 1.446 diff -u -u -r1.446 server.c --- src/server.c 8 Sep 2005 18:40:20 -0000 1.446 +++ src/server.c 21 Sep 2005 07:32:05 -0000 @@ -38,10 +38,6 @@ * * FIXME - They should be in a different file. */ -/* We use Kerberos 5 routines to map the GSSAPI credential to a user - name. */ -# include - static void gserver_authenticate_connection (void); /* Whether we are already wrapping GSSAPI communication. */ @@ -7362,13 +7358,17 @@ { char *hn; gss_buffer_desc tok_in, tok_out; + gss_buffer_desc gss_status, mech_status; char buf[1024]; char *credbuf; size_t credbuflen; - OM_uint32 stat_min, ret; + OM_uint32 stat_maj, stat_min, ret; + OM_uint32 new_stat_min; + OM_uint32 message_context; gss_name_t server_name, client_name; gss_cred_id_t server_creds; int nbytes; + int i; gss_OID mechid; hn = canon_host (server_hostname); @@ -7396,10 +7396,12 @@ /* The client will send us a two byte length followed by that many bytes. */ - if (fread (buf, 1, 2, stdin) != 2) + if (buf_read_data(buf_from_net, 1, &credbuf, &credbuflen) < 0) error (1, errno, "read of length failed"); - - nbytes = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff); + nbytes = (*credbuf & 0xff) << 8; + if (buf_read_data(buf_from_net, 1, &credbuf, &credbuflen) < 0) + error (1, errno, "read of length failed"); + nbytes |= *credbuf & 0xff; if (nbytes <= sizeof buf) { credbuf = buf; @@ -7411,14 +7413,33 @@ credbuf = xmalloc (credbuflen); } - if (fread (credbuf, 1, nbytes, stdin) != nbytes) - error (1, errno, "read of data failed"); + for (i = 0; i < nbytes;) + { + char *b; + size_t l; + + if (buf_read_data(buf_from_net, nbytes - i, &b, &l) < 0) + error (1, errno, "read of data failed"); + memcpy(credbuf + i, b, l); + i += l; + } gcontext = GSS_C_NO_CONTEXT; tok_in.length = nbytes; tok_in.value = credbuf; - if (gss_accept_sec_context (&stat_min, + /* + * Our GSS-API server compatible only with Kerberos. + * + * We made only one call gss_init_sec_context() per + * connection and one call gss_display_status() per + * messages. + * + * TODO: Strict conformity to GSS-API, see example + * . Need SPKI or + * other non Kerberos GSS-API implementation. + */ + stat_maj = gss_accept_sec_context (&stat_min, &gcontext, /* context_handle */ server_creds, /* verifier_cred_handle */ &tok_in, /* input_token */ @@ -7428,30 +7449,121 @@ &tok_out, /* output_token */ &ret, NULL, /* ignore time_rec */ - NULL) /* ignore del_cred_handle */ - != GSS_S_COMPLETE) + NULL); /* ignore del_cred_handle */ + message_context = 0; + gss_display_status (&new_stat_min, stat_maj, GSS_C_GSS_CODE, + mechid, &message_context, &gss_status); + message_context = 0; + gss_display_status (&new_stat_min, stat_min, GSS_C_MECH_CODE, + mechid, &message_context, &mech_status); + +# if HAVE_SYSLOG_H + syslog (LOG_DAEMON | (stat_maj != GSS_S_COMPLETE ? LOG_ERR : LOG_DEBUG), + "GSSAPI: %si/%s", + (const char *) gss_status.value, + (const char *) mech_status.value); +# endif /* HAVE_SYSLOG_H */ + if (stat_maj != GSS_S_COMPLETE) { - error (1, 0, "could not verify credentials"); - } +# ifdef __sun + /* + * Default Solaris configuration - use diffie_hellman_640_0 + * Probably common mistake + */ +# if HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, "Check /etc/gss/mech; man mech"); +# endif /* HAVE_SYSLOG_H */ +# endif /* __sun */ + error (1, 0, "GSSAPI authentication failed: %s/%s", + (const char *) gss_status.value, + (const char *) mech_status.value); + } + + gss_release_buffer(&new_stat_min, &gss_status); + gss_release_buffer(&new_stat_min, &mech_status); + + { + /* Unfortunatly GSS-API can only import GSS_C_NT_USER_NAME + 1. We parse 'Kerberos Principial Name' manualy, + as described by RFC 1964 + 2. Import result as GSS_C_NT_USER_NAME; + 3. Canonicalize it to mechanism OID; + 4. Compare its for check result name type is GSS_C_NT_USER_NAME; + + See + */ + OM_uint32 smaj, smin, tmin; + gss_buffer_desc kname; + gss_OID kmechoid; + gss_name_t iname, cname; + char *realm, *princ; + int match = 0; - /* FIXME: Use Kerberos v5 specific code to authenticate to a user. - We could instead use an authentication to access mapping. */ - { - krb5_context kc; - krb5_principal p; - gss_buffer_desc desc; + smaj = gss_display_name (&smin, client_name, &kname, &kmechoid); + if (smaj != GSS_S_COMPLETE) + { + goto name_err; + } + princ = kname.value; + + /* According (2) realm don't have '@' character */ + if (NULL == (realm = strrchr (princ, '@')) + || realm == princ || realm[-1] == '\\') + { + realm = princ + strlen (princ); + } + if ((realm - princ) >= (sizeof (buf) - 1)) + { + match = 0; + gss_release_buffer(&tmin, &kname); + goto name_err; + } + memcpy (buf, princ, realm - princ); + buf[realm - princ] = '\0'; + (void)gss_release_buffer(&tmin, &kname); + + kname.value = buf; + kname.length = strlen(buf); + smaj = gss_import_name(&smin, &kname, GSS_C_NT_USER_NAME, &iname); + if (smaj != GSS_S_COMPLETE) + { + goto name_err; + } - 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) + smaj = gss_canonicalize_name (&smin, iname, mechid, &cname); + if (smaj != GSS_S_COMPLETE) { - error (1, 0, "access denied"); + (void)gss_release_name (&tmin, &iname); + goto name_err; } - krb5_free_principal (kc, p); - krb5_free_context (kc); + + smaj = gss_compare_name (&smin, cname, client_name, &match); + (void)gss_release_name (&tmin, &iname); + (void)gss_release_name (&tmin, &cname); +name_err: + if (smaj != GSS_S_COMPLETE || !match) + { + message_context = 0; + (void)gss_display_status (&tmin, smaj, GSS_C_GSS_CODE, + mechid, &message_context, &gss_status); + message_context = 0; + (void)gss_display_status (&tmin, smin, GSS_C_MECH_CODE, + mechid, &message_context, &mech_status); +# if HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, + "access denied: %si/%s", + (const char *) gss_status.value, + (const char *) mech_status.value); +# endif /* HAVE_SYSLOG_H */ + error (1, 0, "access denied: %s/%s", + (const char *) gss_status.value, + (const char *) mech_status.value); + (void)gss_release_buffer(&tmin, &gss_status); + (void)gss_release_buffer(&tmin, &mech_status); + } +# if HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_DEBUG, "gss_userok: %s", buf); +# endif /* HAVE_SYSLOG_H */ } if (tok_out.length != 0) @@ -7460,10 +7572,10 @@ 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"); + buf_output(buf_to_net, cbuf, 2); + buf_output(buf_to_net, tok_out.value, tok_out.length); + if (buf_flush (buf_to_net, false)) + error (1, errno, "buf_flush failed"); } switch_to_user ("GSSAPI", buf); @@ -7471,7 +7583,8 @@ if (credbuf != buf) free (credbuf); - printf ("I LOVE YOU\n"); + buf_output0 (buf_to_net, "I LOVE YOU\n"); + buf_flush (buf_to_net, true); fflush (stdout); }