From: F Harvell Subject: New Kerberos Version 5 support in CVS 1.9.8 To: bug-cvs@prep.ai.mit.edu Date: Mon, 28 Apr 1997 18:30:54 -0400 (EDT) Hello all, I have patched the CVS version 1.9.8 release to support authentication with the new Kerberos version 5 release 1.0. These patches are attached below. For brievity, I have not included the modified configure script, please "autoconf" after applying the patches. (Note, these patches support authentication only, encryption is not supported.) I coded the patches following the devel-cvs guide and with the intent of having them incorporated into the main CVS tree. After speaking with some folks, they have suggested that I convert over to the GSSAPI. The patches that are attached accomplish our immediate needs and any further work will need to be done as a lower priority. If anyone uses these patches and/or is interested in more krb5 (GSSAPI) support in cvs, _please_ send me some e-mail so that I can prioritize my future efforts. Note: I expect that full support (including encryption) for krb5 will come with the switch to GSSAPI. I also expect that I will just add support for the GSSAPI instead of backing out the (working) krb5 code. Thanks, F Harvell -- Mr. F Harvell Phone: 407 657-2202 x205 MagicNet, Incorporated Fax: 407 679-8562 10149 University Blvd E-mail: fharvell@magicnet.net Orlando, FL 32817 fharvell@ocelot.oau.org ----- cut ----- diff -r -c cvs-1.9.6.orig/ChangeLog cvs-1.9.6/ChangeLog *** cvs-1.9.6.orig/ChangeLog Wed Mar 19 16:58:57 1997 --- cvs-1.9.6/ChangeLog Fri Apr 18 09:56:40 1997 *************** *** 1,3 **** --- 1,14 ---- + Fri Apr 18 09:18:34 EDT 1997 F Harvell + + * Added support for Kerberos version 5 authentication. + New option --with-krb5 added to configure (configure.in). + Added support for HAVE_KERBEROS_5 define (config.h.in). + Added new file (src/kserver.h) to hold the krb5 service + and version information. Modified files (src/client.c + and src/server.c) to support krb5 authentication. + * Note that this iteration of krb5 support does NOT + support encryption. I anticipate adding it later. + Wed Mar 19 14:06:40 1997 Jim Meyering * configure.in (test for shadow passwords): Use AC_MSG_RESULT diff -r -c cvs-1.9.6.orig/NEWS cvs-1.9.6/NEWS *** cvs-1.9.6.orig/NEWS Sun Mar 2 22:28:55 1997 --- cvs-1.9.6/NEWS Fri Apr 18 09:56:32 1997 *************** *** 1,5 **** --- 1,9 ---- Changes since 1.9: + * Added support for Kerberos version 5 authentication. New option + "--with-krb5" added to configure. ** Note ** This iteration of krb5 + support does NOT support encryption. I anticipate adding it later. + * "cvs admin [options]" will now recurse. In previous versions of CVS, it was an error and one needed to specify "cvs admin [options] ." to recurse. This change brings admin in line with the other CVS diff -r -c cvs-1.9.6.orig/config.h.in cvs-1.9.6/config.h.in *** cvs-1.9.6.orig/config.h.in Thu Mar 13 08:40:39 1997 --- cvs-1.9.6/config.h.in Thu Apr 17 12:08:36 1997 *************** *** 59,64 **** --- 59,67 ---- /* Define if you have MIT Kerberos version 4 available. */ #undef HAVE_KERBEROS + /* Define if you have MIT Kerberos version 5 available. */ + #undef HAVE_KERBEROS_5 + /* Define if you want CVS to be able to be a remote repository client. */ #undef CLIENT_SUPPORT diff -r -c cvs-1.9.6.orig/configure.in cvs-1.9.6/configure.in *** cvs-1.9.6.orig/configure.in Wed Mar 19 16:58:57 1997 --- cvs-1.9.6/configure.in Fri Apr 18 10:17:29 1997 *************** *** 189,196 **** AC_SUBST(includeopt) fi fi fi - AC_CHECK_FUNCS(krb_get_err_text) dnl dnl Use --with-encryption to turn on encryption support --- 189,259 ---- AC_SUBST(includeopt) fi fi + AC_CHECK_FUNCS(krb_get_err_text) + fi + + dnl + dnl set $(KRB5) from --with-krb5=value -- WITH_KRB5 + dnl + KRB5=/usr/kerberos + define(WITH_KRB5,[ + AC_ARG_WITH([krb5], + [ --with-krb5=value set default \$(KRB5) from value], + [KRB5=$withval], + )dnl + echo "default place for krb5 is $KRB5" + AC_SUBST(KRB5)])dnl + WITH_KRB5 + + krb5_h= + AC_MSG_CHECKING([for krb5.h]) + AC_TRY_LINK([#include ],[int i;], + [krb5_h=yes krb5_incdir=], + [if test "$cross_compiling" != yes && test -r $KRB5/include/krb5.h; then + hold_cflags=$CFLAGS + CFLAGS="$CFLAGS -I$KRB5/include" + AC_TRY_LINK([#include ],[int i;], + [krb5_h=yes krb5_incdir=$KRB5/include]) + CFLAGS=$hold_cflags + fi]) + if test -z "$krb5_h"; then + AC_TRY_LINK([#include ],[int i;], + [krb5_h=yes krb5_incdir=], + [if test "$cross_compiling" != yes && test -r $KRB5/include/kerberosV/krb5.h; then + hold_cflags=$CFLAGS + CFLAGS="$CFLAGS -I$KRB5/include/kerberosV" + AC_TRY_LINK([#include ],[int i;], + [krb5_h=yes krb5_incdir=$KRB5/include/kerberosV]) + CFLAGS=$hold_cflags + fi]) + fi + AC_MSG_RESULT($krb5_h) + + if test -n "$krb5_h"; then + krb5_lib= + AC_CHECK_LIB(krb5,printf,[krb5_lib=yes krb5_libdir=], + [if test "$cross_compiling" != yes && test -r $KRB5/lib/libkrb5.a; then + krb5_lib=yes krb5_libdir=$KRB5/lib + fi]) + if test -n "$krb5_lib"; then + AC_DEFINE(HAVE_KERBEROS) + AC_DEFINE(HAVE_KERBEROS_5) + test -n "${krb5_libdir}" && LIBS="${LIBS} -L${krb5_libdir}" + LIBS="${LIBS} -lkrb5" + # Put -L${krb5_libdir} in LDFLAGS temporarily so that it appears before + # -ldes in the command line. Don't do it permanently so that we honor + # the user's setting for LDFLAGS + hold_ldflags=$LDFLAGS + test -n "${krb5_libdir}" && LDFLAGS="$LDFLAGS -L${krb5_libdir}" + AC_CHECK_LIB(crypto,printf,[LIBS="${LIBS} -lcrypto"]) + AC_CHECK_LIB(com_err,printf,[LIBS="${LIBS} -lcom_err"]) + LDFLAGS=$hold_ldflags + if test -n "$krb5_incdir"; then + includeopt="${includeopt} -I$krb5_incdir" + AC_SUBST(includeopt) + fi + fi fi dnl dnl Use --with-encryption to turn on encryption support *************** *** 204,210 **** esac], [encryption=false]) if test "$encryption" = "true"; then ! AC_DEFINE(ENCRYPTION) fi AC_CHECK_FUNC(gethostname, :, LIBOBJS="$LIBOBJS hostname.o") --- 267,277 ---- esac], [encryption=false]) if test "$encryption" = "true"; then ! if test -n "$krb5_lib"; then ! AC_MSG_ERROR(encryption not currently supported with krb5) ! else ! AC_DEFINE(ENCRYPTION) ! fi fi AC_CHECK_FUNC(gethostname, :, LIBOBJS="$LIBOBJS hostname.o") diff -r -c cvs-1.9.6.orig/src/ChangeLog cvs-1.9.6/src/ChangeLog *** cvs-1.9.6.orig/src/ChangeLog Tue Mar 25 13:27:00 1997 --- cvs-1.9.6/src/ChangeLog Fri Apr 18 10:23:27 1997 *************** *** 1,3 **** --- 1,10 ---- + Fri Apr 18 09:18:34 EDT 1997 F Harvell + + * kserver.h added to hold krb5 service and version information. + * client.c and src/server.c modified to support krb5 + authentication. Note: this iteration of krb5 support does + NOT support encryption. I anticipate adding it later. + Tue Mar 25 13:26:52 1997 Jim Kingdon * version.c: Version 1.9.6. diff -r -c cvs-1.9.6.orig/src/client.c cvs-1.9.6/src/client.c *** cvs-1.9.6.orig/src/client.c Thu Mar 20 16:58:27 1997 --- cvs-1.9.6/src/client.c Fri Apr 18 14:37:07 1997 *************** *** 27,45 **** #if HAVE_KERBEROS || USE_DIRECT_TCP #define CVS_PORT 1999 ! #if HAVE_KERBEROS #include extern char *krb_realmofhost (); ! #ifndef HAVE_KRB_GET_ERR_TEXT #define krb_get_err_text(status) krb_err_txt[status] ! #endif /* HAVE_KRB_GET_ERR_TEXT */ /* Information we need if we are going to use Kerberos encryption. */ static C_Block kblock; static Key_schedule sched; ! #endif /* HAVE_KERBEROS */ #endif /* HAVE_KERBEROS || USE_DIRECT_TCP */ --- 27,59 ---- #if HAVE_KERBEROS || USE_DIRECT_TCP #define CVS_PORT 1999 ! # ifdef HAVE_KERBEROS ! # ifdef HAVE_KERBEROS_5 ! #include ! #include ! #include "kserver.h" ! ! /* Information we need if we are going to use Kerberos encryption. */ ! #define CRYPT_BUFSIZ 5120 ! char des_inbuf[2*CRYPT_BUFSIZ]; /* needs to be > largest read size */ ! char des_outpkt[2*CRYPT_BUFSIZ+4]; /* needs to be > largest write size */ ! krb5_data desinbuf,desoutbuf; ! krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ ! ! # else /* ! HAVE_KERBEROS_5 */ #include extern char *krb_realmofhost (); ! # ifndef HAVE_KRB_GET_ERR_TEXT #define krb_get_err_text(status) krb_err_txt[status] ! # endif /* HAVE_KRB_GET_ERR_TEXT */ /* Information we need if we are going to use Kerberos encryption. */ static C_Block kblock; static Key_schedule sched; ! # endif /* ! HAVE_KERBEROS_5 */ ! # endif /* HAVE_KERBEROS */ #endif /* HAVE_KERBEROS || USE_DIRECT_TCP */ *************** *** 3187,3200 **** struct sockaddr_in sin; int s; ! #if HAVE_KERBEROS KTEXT_ST ticket; const char *realm; - #endif /* HAVE_KERBEROS */ - int status; ! ! #ifndef HAVE_KERBEROS /* It is a crock to have :kserver: sometimes mean kerberos, and sometimes mean "direct tcp", based on USE_DIRECT_TCP. If we need the "direct tcp" stuff, we need a new access method, --- 3201,3223 ---- struct sockaddr_in sin; int s; ! #ifdef HAVE_KERBEROS ! # ifdef HAVE_KERBEROS_5 ! krb5_context context; ! krb5_data cksum_data; ! krb5_error_code retval; ! krb5_ccache ccdef; ! krb5_principal client, server; ! krb5_error *err_ret; ! krb5_ap_rep_enc_part *rep_ret; ! krb5_auth_context auth_context = NULL; ! char *service = CVS_KSERVICE; ! # else /* ! HAVE_KERBEROS_5 */ KTEXT_ST ticket; const char *realm; int status; ! # endif /* ! HAVE_KERBEROS_5 */ ! #else /* ! HAVE_KERBEROS */ /* It is a crock to have :kserver: sometimes mean kerberos, and sometimes mean "direct tcp", based on USE_DIRECT_TCP. If we need the "direct tcp" stuff, we need a new access method, *************** *** 3216,3223 **** hname = xmalloc (strlen (hp->h_name) + 1); strcpy (hname, hp->h_name); ! #if HAVE_KERBEROS realm = krb_realmofhost (hname); #endif /* HAVE_KERBEROS */ /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */ --- 3239,3268 ---- hname = xmalloc (strlen (hp->h_name) + 1); strcpy (hname, hp->h_name); ! #ifdef HAVE_KERBEROS ! # ifdef HAVE_KERBEROS_5 ! /* initialize the kerberos 5 process context */ ! if ((retval = krb5_init_context(&context))) { ! com_err(program_name, retval, "while initializing krb5"); ! error (1, 0, "cvs exiting."); ! } ! ! /* check the checksum */ ! if (!valid_cksumtype(CKSUMTYPE_CRC32)) { ! com_err(program_name, KRB5_PROG_SUMTYPE_NOSUPP, "while using CRC-32"); ! error (1, 0, "cvs exiting."); ! } ! ! /* lookup the service principal */ ! if ((retval = krb5_sname_to_principal(context, hname, service, ! KRB5_NT_SRV_HST, &server))) { ! com_err(program_name, retval, "while creating server name for %s/%s", ! service, hname); ! error (1, 0, "cvs exiting."); ! } ! # else /* ! HAVE_KERBEROS_5 */ realm = krb_realmofhost (hname); + # endif /* ! HAVE_KERBEROS_5 */ #endif /* HAVE_KERBEROS */ /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */ *************** *** 3272,3284 **** --- 3317,3370 ---- #ifdef HAVE_KERBEROS struct sockaddr_in laddr; int laddrlen; + # ifndef HAVE_KERBEROS_5 MSG_DAT msg_data; CREDENTIALS cred; + # endif /* HAVE_KERBEROS_5 */ laddrlen = sizeof (laddr); if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0) error (1, errno, "getsockname"); + # ifdef HAVE_KERBEROS_5 + cksum_data.data = hname; + cksum_data.length = strlen(hname); + + /* get the default credentials cache */ + if ((retval = krb5_cc_default(context, &ccdef))) { + com_err(program_name, retval, "while getting default ccache"); + error (1, 0, "cvs exiting."); + } + + /* get the client principal name */ + if ((retval = krb5_cc_get_principal(context, ccdef, &client))) { + com_err(program_name, retval, "while getting client principal name"); + error (1, 0, "cvs exiting."); + } + + /* send the auth */ + retval = krb5_sendauth(context, &auth_context, (krb5_pointer) &s, + CVS_KVERSION, client, server, + AP_OPTS_MUTUAL_REQUIRED, + &cksum_data, + 0, /* no creds, use ccache instead */ + ccdef, &err_ret, &rep_ret, NULL); + + /* finished with the principal */ + krb5_free_principal(context, server); + + /* check for a valid auth */ + if (retval) { + com_err(program_name, retval, "while using sendauth"); + if (retval == KRB5_SENDAUTH_REJECTED) { + error (0, 0, "Error text sent from server: %*s", + err_ret->text.length, err_ret->text.data); + } + error (1, 0, "cvs exiting."); + } + + # else /* ! HAVE_KERBEROS_5 */ + /* We don't care about the checksum, and pass it as zero. */ status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd", hname, realm, (unsigned long) 0, &msg_data, *************** *** 3292,3306 **** { memcpy (kblock, cred.session, sizeof (C_Block)); #endif /* HAVE_KERBEROS */ server_fd = s; close_on_exec (server_fd); tofd = fromfd = s; ! #ifdef HAVE_KERBEROS } ! #endif /* HAVE_KERBEROS */ } if (tofd == -1) --- 3378,3393 ---- { memcpy (kblock, cred.session, sizeof (C_Block)); + # endif /* ! HAVE_KERBEROS_5 */ #endif /* HAVE_KERBEROS */ server_fd = s; close_on_exec (server_fd); tofd = fromfd = s; ! #if defined(HAVE_KERBEROS) && ! defined(HAVE_KERBEROS_5) } ! #endif /* defined(HAVE_KERBEROS) && ! defined(HAVE_KERBEROS_5) */ } if (tofd == -1) diff -r -c cvs-1.9.6.orig/src/kserver.h cvs-1.9.6/src/kserver.h *** cvs-1.9.6.orig/src/kserver.h Fri Apr 18 09:02:02 1997 --- cvs-1.9.6/src/kserver.h Thu Apr 17 14:47:42 1997 *************** *** 0 **** --- 1,9 ---- + /* Common definitions for kerberos client/server. */ + + #ifndef KRB5_CVS__ + #define KRB5_CVS__ + + #define CVS_KSERVICE "cvs" + #define CVS_KVERSION "cvskserver_v2.0" + + #endif /* KRB5_CVS__ */ diff -r -c cvs-1.9.6.orig/src/server.c cvs-1.9.6/src/server.c *** cvs-1.9.6.orig/src/server.c Sun Mar 16 10:47:32 1997 --- cvs-1.9.6/src/server.c Thu Apr 17 16:15:07 1997 *************** *** 28,43 **** #ifdef HAVE_KERBEROS #include #include ! #ifndef HAVE_KRB_GET_ERR_TEXT #define krb_get_err_text(status) krb_err_txt[status] ! #endif /* Information we need if we are going to use Kerberos encryption. */ static C_Block kblock; static Key_schedule sched; ! #endif /* for select */ #include --- 28,58 ---- #ifdef HAVE_KERBEROS #include + # ifdef HAVE_KERBEROS_5 + #include + #include + #include "kserver.h" + + /* Information we need if we are going to use Kerberos encryption. */ + #define CRYPT_BUFSIZ 5120 + char des_inbuf[2*CRYPT_BUFSIZ]; /* needs to be > largest read size */ + char des_outpkt[2*CRYPT_BUFSIZ+4]; /* needs to be > largest write size */ + krb5_data desinbuf,desoutbuf; + krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ + + # else /* ! HAVE_KERBEROS_5 */ #include ! ! # ifndef HAVE_KRB_GET_ERR_TEXT #define krb_get_err_text(status) krb_err_txt[status] ! # endif /* HAVE_KRB_GET_ERR_TEXT */ /* Information we need if we are going to use Kerberos encryption. */ static C_Block kblock; static Key_schedule sched; ! # endif /* ! HAVE_KERBEROS_5 */ ! #endif /* HAVE_KERBEROS */ /* for select */ #include *************** *** 4561,4577 **** void kserver_authenticate_connection () { - int status; - char instance[INST_SZ]; struct sockaddr_in peer; struct sockaddr_in laddr; int len; KTEXT_ST ticket; AUTH_DAT auth; char version[KRB_SENDAUTH_VLEN]; char user[ANAME_SZ]; - strcpy (instance, "*"); len = sizeof peer; if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &len) < 0 || getsockname (STDIN_FILENO, (struct sockaddr *) &laddr, --- 4576,4603 ---- void kserver_authenticate_connection () { struct sockaddr_in peer; struct sockaddr_in laddr; int len; + # ifdef HAVE_KERBEROS_5 + krb5_context context; + krb5_auth_context auth_context = NULL; + krb5_ticket * ticket; + int sock = STDIN_FILENO; /* assumes that server is started from inetd */ + krb5_error_code retval; + krb5_principal server; + char *service = CVS_KSERVICE; + krb5_keytab keytab = NULL; /* hook to allow specification */ + char user[L_cuserid + 1]; + # else /* ! HAVE_KERBEROS_5 */ + int status; + char instance[INST_SZ]; KTEXT_ST ticket; AUTH_DAT auth; char version[KRB_SENDAUTH_VLEN]; char user[ANAME_SZ]; + # endif /* ! HAVE_KERBEROS_5 */ len = sizeof peer; if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &len) < 0 || getsockname (STDIN_FILENO, (struct sockaddr *) &laddr, *************** *** 4598,4603 **** --- 4624,4683 ---- } #endif + # ifdef HAVE_KERBEROS_5 + /* initialize the kerberos 5 process context */ + if ((retval = krb5_init_context(&context))) { + com_err(program_name, retval, "while initializing krb5"); + #ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); + #endif + exit(EXIT_FAILURE); + } + + /* lookup the service principal */ + if ((retval = krb5_sname_to_principal(context, NULL, service, + KRB5_NT_SRV_HST, &server))) { + com_err(program_name, retval, "while generating service name (%s)", + service); + #ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); + #endif + exit(EXIT_FAILURE); + } + + /* receive the auth */ + if ((retval = krb5_recvauth(context, &auth_context, (krb5_pointer)&sock, + CVS_KVERSION, server, + 0, /* no flags */ + keytab, /* default keytab is NULL */ + &ticket))) { + com_err(program_name, retval, "recvauth failed"); + #ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); + #endif + exit (EXIT_FAILURE); + } + + /* Get client localname (username) */ + if ((retval = krb5_aname_to_localname(context,ticket->enc_part2->client, + L_cuserid, &user))){ + com_err(program_name, retval, "aname to localname failed"); + #ifdef SYSTEM_CLEANUP + /* Hook for OS-specific behavior, for example socket subsystems on + NT and OS2 or dealing with windows and arguments on Mac. */ + SYSTEM_CLEANUP (); + #endif + exit (EXIT_FAILURE); + } + + # else /* ! HAVE_KERBEROS_5 */ + strcpy (instance, "*"); status = krb_recvauth (KOPT_DO_MUTUAL, STDIN_FILENO, &ticket, "rcmd", instance, &peer, &laddr, &auth, "", sched, version); *************** *** 4621,4626 **** --- 4701,4707 ---- { printf ("E Fatal error, aborting.\n\ error 0 kerberos: can't get local name: %s\n", krb_get_err_text(status)); + #ifdef SYSTEM_CLEANUP /* Hook for OS-specific behavior, for example socket subsystems on NT and OS2 or dealing with windows and arguments on Mac. */ *************** *** 4628,4633 **** --- 4709,4715 ---- #endif exit (EXIT_FAILURE); } + # endif /* HAVE_KERBEROS_5 */ /* Switch to run as this user. */ switch_to_user (user);