On Mon, Mar 05, 2001 at 03:20:18PM +0100, Assar Westerlund wrote:
> "Jacques A. Vidrine" <[EMAIL PROTECTED]> writes:
> > Will we always have this dichotomy between kblah/k5blah utilities?  It is
> > fairly annoying.  Anecdotally, there don't seem to be many new Kerberos
> > IV installations, & Kerberos V's utilities can get/list/trash version 4
> > & 5 tickets.
> 
> Yes.  My current plan of things to happen (in -current and after 4.3)
> is to ditch the v4 programs basically and install the v5 ones under
> the canonical names (with v4-compatibility if compiled in).  And to
> always build and install the krb5 programs/libraries, but ifdef'ing
> the support in other programs.  How does that sounds?

That's what I wanted to hear :-)


By the way.  In my faithlessness, I did not expect that 0.3e would be
MFC'd in time for 4.3.  I'm pleasantly surprised.  However, I would be
sorry that 4.3 shipped without a pam_kerberos5.  I am preparing to
leave on holiday, and will not be able to make it happen myself.  In
the hopes that someone else can pick up the pieces, I am attaching a
shar that I threw together today.  I'm pretty sure that I don't have
the dependencies right, but I can't play with a build world at the
moment.

This contains a pam_kerberos5 module derived from fcusack's (as found
in ports/security/pam_krb5).  Unfortunately, it does not appear that
fcusack is maintaining this.  I've been maintaining it on an
unofficial basis, initially fixing several bugs and getting it to
build with Heimdal, and then integrating fixes from the Kerberos
mailings lists.  In fact, some Linux distributions now apparently use
this `version' from ports/security/pam_krb5.

In short, I think it is appropriate to add it to the source tree so
that it will be maintained.  The license is dual BSD/GPL.  If this
misses 4.3 it is not the end of the world, but it would be nice.  I've
been using the port with consoles, sshd, and xdm (actually wdm) for
four months or so.

Cheers,
-- 
Jacques Vidrine / [EMAIL PROTECTED] / [EMAIL PROTECTED] / [EMAIL PROTECTED]

# This file contains patches and a shar archive.  I expect it to
# be invoked as follows:
#
#   % (cd /usr/src && /bin/sh ${HOME}/pam_kerberos5.shar)
#
echo p - share/mk/bsd.libnames.mk
(sed 's/^X//' |/usr/bin/patch -Ns) << 'END-of-share/mk/bsd.libnames.mk'
X--- share/mk/bsd.libnames.mk.orig      Sun Mar  4 10:16:23 2001
X+++ share/mk/bsd.libnames.mk   Mon Mar  5 11:37:33 2001
X@@ -10,6 +10,7 @@
X LIBKZTAIL?=   ${DESTDIR}${LIBDIR}/kztail.o
X 
X LIBALIAS?=    ${DESTDIR}${LIBDIR}/libalias.a
X+LIBASN1?=     ${DESTDIR}${LIBDIR}/libasn1.a # XXX in secure dist, not base
X LIBATM?=      ${DESTDIR}${LIBDIR}/libatm.a
X LIBC?=                ${DESTDIR}${LIBDIR}/libc.a
X LIBC_PIC=     ${DESTDIR}${LIBDIR}/libc_pic.a
X@@ -35,12 +36,14 @@
X LIBGCC_PIC?=  ${DESTDIR}${LIBDIR}/libgcc_pic.a
X LIBGMP?=      ${DESTDIR}${LIBDIR}/libgmp.a
X LIBGNUREGEX?= ${DESTDIR}${LIBDIR}/libgnuregex.a
X+LIBGSSAPI?=   ${DESTDIR}${LIBDIR}/libgssapi.a # XXX in secure dist, not base
X LIBHISTORY?=  ${DESTDIR}${LIBDIR}/libhistory.a
X LIBIPSEC?=    ${DESTDIR}${LIBDIR}/libipsec.a
X LIBIPX?=      ${DESTDIR}${LIBDIR}/libipx.a
X LIBISC?=      ${DESTDIR}${LIBDIR}/libisc.a
X LIBKDB?=      ${DESTDIR}${LIBDIR}/libkdb.a    # XXX in secure dist, not base
X LIBKRB?=      ${DESTDIR}${LIBDIR}/libkrb.a    # XXX in secure dist, not base
X+LIBKRB5?=     ${DESTDIR}${LIBDIR}/libkrb5.a   # XXX in secure dist, not base
X LIBKEYCAP?=   ${DESTDIR}${LIBDIR}/libkeycap.a
X LIBKVM?=      ${DESTDIR}${LIBDIR}/libkvm.a
X LIBL?=                ${DESTDIR}${LIBDIR}/libl.a
X@@ -76,6 +79,7 @@
X LIBRADIUS?=   ${DESTDIR}${LIBDIR}/libradius.a
X LIBREADLINE?= ${DESTDIR}${LIBDIR}/libreadline.a
X LIBRESOLV?=   ${DESTDIR}${LIBDIR}/libresolv.a # XXX doesn't exist
X+LIBROKEN?=    ${DESTDIR}${LIBDIR}/libroken.a # XXX in secure dist, not base
X LIBRPCSVC?=   ${DESTDIR}${LIBDIR}/librpcsvc.a
X LIBSCRYPT?=   "don't use LIBSCRYPT, use LIBCRYPT"
X LIBDESCRYPT?= "don't use LIBDESCRYPT, use LIBCRYPT"
END-of-share/mk/bsd.libnames.mk
echo p - lib/libpam/modules/Makefile
(sed 's/^X//' |/usr/bin/patch -Ns) << 'END-of-lib/libpam/modules/Makefile'
X--- lib/libpam/modules/Makefile.orig   Sun Mar  4 10:37:31 2001
X+++ lib/libpam/modules/Makefile        Mon Mar  5 12:01:39 2001
X@@ -29,7 +29,7 @@
X .if defined(MAKE_KERBEROS4) && !defined(NOCRYPT) && !defined(NO_OPENSSL)
X SUBDIR+=      pam_kerberosIV
X .endif
X-.if defined(MAKE_KERBEROS5__) && !defined(NOCRYPT) && !defined(NO_OPENSSL)
X+.if defined(MAKE_KERBEROS5) && !defined(NOCRYPT) && !defined(NO_OPENSSL)
X SUBDIR+=      pam_kerberos5
X .endif
X SUBDIR+=      pam_opie
END-of-lib/libpam/modules/Makefile
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#       lib/libpam/modules/pam_kerberos5
#       lib/libpam/modules/pam_kerberos5/Makefile
#       lib/libpam/modules/pam_kerberos5/pam_krb5_auth.c
#       lib/libpam/modules/pam_kerberos5/pam_krb5_sess.c
#       lib/libpam/modules/pam_kerberos5/pam_krb5_pass.c
#       lib/libpam/modules/pam_kerberos5/support.c
#       lib/libpam/modules/pam_kerberos5/pam_krb5_acct.c
#       lib/libpam/modules/pam_kerberos5/compat_mit.c
#       lib/libpam/modules/pam_kerberos5/compat_heimdal.c
#       lib/libpam/modules/pam_kerberos5/pam_krb5.h
#       lib/libpam/modules/pam_kerberos5/COPYRIGHT
#       lib/libpam/modules/pam_kerberos5/README.fcusack
#
echo c - lib/libpam/modules/pam_kerberos5
mkdir -p lib/libpam/modules/pam_kerberos5 > /dev/null 2>&1
echo x - lib/libpam/modules/pam_kerberos5/Makefile
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/Makefile << 
'END-of-lib/libpam/modules/pam_kerberos5/Makefile'
X# Copyright 2001 Jacques Vidrine
X# All rights reserved.
X#
X# Redistribution and use in source and binary forms, with or without
X# modification, are permitted provided that the following conditions
X# are met:
X# 1. Redistributions of source code must retain the above copyright
X#    notice, this list of conditions and the following disclaimer.
X# 2. Redistributions in binary form must reproduce the above copyright
X#    notice, this list of conditions and the following disclaimer in the
X#    documentation and/or other materials provided with the distribution.
X#
X# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X# SUCH DAMAGE.
X#
X# $FreeBSD$
X
XPAMDIR=                ${.CURDIR}/../../../../contrib/libpam
XKRB5DIR=       ${.CURDIR}/../../../../crypto/heimdal
X
X.if exists(${.OBJDIR}/../../../../kerberos5/lib)
XKRB5LIBOBJDIR= ${.OBJDIR}/../../../../kerberos5/lib
X.else
XKRB5LIBOBJDIR= ${.CURDIR}/../../../../kerberos5/lib
X.endif
X
XASN1OBJDIR=     ${KRB5LIBOBJDIR}/libasn1
XKRB5OBJDIR=     ${KRB5LIBOBJDIR}/libkrb5
XROKENOBJDIR=    ${KRB5LIBOBJDIR}/libroken
XGSSAPIOBJDIR=    ${KRB5LIBOBJDIR}/libgssapi
X
XLIB=           pam_kerberos5
XSHLIB_NAME=    pam_kerberos5.so
XSRCS=          compat_heimdal.c pam_krb5_acct.c pam_krb5_auth.c \
X               pam_krb5_pass.c pam_krb5_sess.c support.c
XCFLAGS+=       -W -Wall -ansi -pedantic -Wbad-function-cast -Wcast-align \
X               -Wcast-qual -Wchar-subscripts -Wconversion -Winline \
X               -Wmissing-prototypes -Wnested-externs -Wpointer-arith \
X               -Wredundant-decls -Wshadow -Wstrict-prototypes -Wwrite-strings
XCFLAGS+=       -I${PAMDIR}/libpam/include
XCFLAGS+=       -I${.CURDIR}/../../libpam
XCFLAGS+=       -I${.CURDIR}/../../../../kerberos5/include \
X               -I${KRB5DIR}/lib/krb5 \
X               -I${KRB5DIR}/lib/asn1 \
X               -I${KRB5DIR}/lib/roken \
X               -I${KRB5DIR}/include \
X               -I${KRB5OBJDIR} \
X               -I${ASN1OBJDIR}
XDPADD+=                ${LIBKRB5} ${LIBGSSAPI} ${LIBASN1} ${LIBROKEN} ${LIBCRYPTO} \
X               ${LIBCRYPT} ${LIBCOM_ERR}
XLDADD+=                -L${KRB5OBJDIR} -L${ROKENOBJDIR} -L${ASN1OBJDIR} \
X               -L${GSSAPIOBJDIR} \
X               -lkrb5 -lgssapi -lasn1 -lroken -lcrypto -lcrypt
XINTERNALLIB=   yes
XINTERNALSTATICLIB=yes
XINCLUDES=      ${KRB5DIR}/lib/krb5/krb5.h \
X               ${.CURDIR}/../../../../include/krb5-types.h \
X               ${KRB5DIR}/lib/krb5/krb5-protos.h heim_err.h krb5_err.h
X
X.include <bsd.lib.mk>
END-of-lib/libpam/modules/pam_kerberos5/Makefile
echo x - lib/libpam/modules/pam_kerberos5/pam_krb5_auth.c
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/pam_krb5_auth.c << 
'END-of-lib/libpam/modules/pam_kerberos5/pam_krb5_auth.c'
X/*
X * pam_krb5_auth.c
X *
X * PAM authentication management functions for pam_krb5
X *
X */
X
Xstatic const char rcsid[] = "$Id: pam_krb5_auth.c,v 1.18 2000/01/04 08:44:08 fcusack 
Exp $";
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <errno.h>
X#include <limits.h>    /* PATH_MAX */
X#include <pwd.h>       /* getpwnam */
X#include <stdio.h>     /* tmpnam */
X#include <stdlib.h>    /* malloc  */
X#include <strings.h>   /* strchr */
X#include <syslog.h>    /* syslog */
X#include <unistd.h>    /* chown */
X
X#include <security/pam_appl.h>
X#include <security/pam_modules.h>
X
X#include <krb5.h>
X#include <com_err.h>
X#include "pam_krb5.h"
X
Xextern krb5_cc_ops krb5_mcc_ops;
X
X/* A useful logging macro */
X#define DLOG(error_func, error_msg) \
Xif (debug) \
X    syslog(LOG_DEBUG, "pam_krb5: pam_sm_authenticate(%s %s): %s: %s", \
X          service, name, error_func, error_msg)
X
X/* Authenticate a user via krb5 */
Xint
Xpam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
X                   const char **argv)
X{
X    krb5_error_code    krbret;
X    krb5_context       pam_context;
X    krb5_creds         creds;
X    krb5_principal     princ;
X    krb5_ccache                ccache, ccache_check;
X    krb5_get_init_creds_opt opts;
X
X    int                        pamret, i;
X    const char         *name;
X    char               *princ_name = NULL;
X    char               *pass = NULL, *service = NULL;
X    char               *prompt = NULL;
X    char               cache_name[L_tmpnam + 8];
X    char               lname[64]; /* local acct name */
X    struct passwd      *pw;
X
X    int debug = 0, try_first_pass = 0, use_first_pass = 0;
X    int forwardable = 0, reuse_ccache = 0, no_ccache = 0;
X
X    for (i = 0; i < argc; i++) {
X       if (strcmp(argv[i], "debug") == 0)
X           debug = 1;
X       else if (strcmp(argv[i], "try_first_pass") == 0)
X           try_first_pass = 1;
X       else if (strcmp(argv[i], "use_first_pass") == 0)
X           use_first_pass = 1;
X       else if (strcmp(argv[i], "forwardable") == 0)
X           forwardable = 1;
X       else if (strcmp(argv[i], "reuse_ccache") == 0)
X           reuse_ccache = 1;
X       else if (strcmp(argv[i], "no_ccache") == 0)
X           no_ccache = 1;
X    }
X
X    /* Get username */
X    if ((pamret = pam_get_user(pamh, &name, "login: ")) != PAM_SUCCESS) {
X       return PAM_SERVICE_ERR;
X    }
X
X    /* Get service name */
X    (void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
X    if (!service)
X       service = "unknown";
X
X    DLOG("entry", "");
X
X    if ((krbret = krb5_init_context(&pam_context)) != 0) {
X       DLOG("krb5_init_context()", error_message(krbret));
X       return PAM_SERVICE_ERR;
X    }
X    krb5_get_init_creds_opt_init(&opts);
X    memset(&creds, 0, sizeof(krb5_creds));
X    memset(cache_name, 0, sizeof(cache_name));
X    memset(lname, 0, sizeof(lname));
X
X    if (forwardable)
X       krb5_get_init_creds_opt_set_forwardable(&opts, 1);
X
X    /* For CNS */
X    if ((krbret = krb5_cc_register(pam_context, &krb5_mcc_ops, FALSE)) != 0) {
X       /* Solaris dtlogin doesn't call pam_end() on failure */
X       if (krbret != KRB5_CC_TYPE_EXISTS) {
X           DLOG("krb5_cc_register()", error_message(krbret));
X           pamret = PAM_SERVICE_ERR;
X           goto cleanup3;
X       }
X    }
X
X    /* Get principal name */
X    if ((krbret = krb5_parse_name(pam_context, name, &princ)) != 0) {
X       DLOG("krb5_parse_name()", error_message(krbret));
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup3;
X    }
X
X    /* Now convert the principal name into something human readable */
X    if ((krbret = krb5_unparse_name(pam_context, princ, &princ_name)) != 0) {
X       DLOG("krb5_unparse_name()", error_message(krbret));
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup2;
X    }
X
X    /* Get password */
X    prompt = malloc(16 + strlen(princ_name));
X    if (!prompt) {
X       DLOG("malloc()", "failure");
X       pamret = PAM_BUF_ERR;
X       goto cleanup2;
X    }
X    (void) sprintf(prompt, "Password for %s: ", princ_name);
X
X    if (try_first_pass || use_first_pass)
X       (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
X
Xget_pass:
X    if (!pass) {
X       try_first_pass = 0;
X       if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF,
X         &pass)) != 0) {
X           DLOG("get_user_info()", pam_strerror(pamh, pamret));
X           pamret = PAM_SERVICE_ERR;
X           goto cleanup2;
X       }
X       /* We have to free pass. */
X       if ((pamret = pam_set_item(pamh, PAM_AUTHTOK, pass)) != 0) {
X           DLOG("pam_set_item()", pam_strerror(pamh, pamret));
X           free(pass);
X           pamret = PAM_SERVICE_ERR;
X           goto cleanup2;
X       }
X       free(pass);
X       /* Now we get it back from the library. */
X       (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
X    }
X
X    /* Verify the local user exists (AFTER getting the password) */
X    if (strchr(name, '@')) {
X       /* get a local account name for this principal */
X       if ((krbret = krb5_aname_to_localname(pam_context, princ, 
X         sizeof(lname), lname)) != 0) {
X           DLOG("krb5_aname_to_localname()", error_message(krbret));
X           pamret = PAM_USER_UNKNOWN;
X           goto cleanup2;
X       }
X       DLOG("changing PAM_USER to", lname);
X       if ((pamret = pam_set_item(pamh, PAM_USER, lname)) != 0) {
X           DLOG("pam_set_item()", pam_strerror(pamh, pamret));
X           pamret = PAM_SERVICE_ERR;
X           goto cleanup2;
X       }
X       if ((pamret = pam_get_item(pamh, PAM_USER, (const void **) &name)
X         != 0)) {
X           DLOG("pam_get_item()", pam_strerror(pamh, pamret));
X           pamret = PAM_SERVICE_ERR;
X           goto cleanup2;
X       }
X    }
X    pw = getpwnam(name);
X    if (!pw) {
X       DLOG("getpwnam()", lname);
X       pamret = PAM_USER_UNKNOWN;
X       goto cleanup2;
X    }
X
X    /* Get a TGT */
X    if ((krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
X      pass, pam_prompter, pamh, 0, NULL, &opts)) != 0) {
X       DLOG("krb5_get_init_creds_password()", error_message(krbret));
X       if (try_first_pass && krbret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
X           pass = NULL;
X           goto get_pass;
X       }
X       pamret = PAM_AUTH_ERR;
X       goto cleanup2;
X    }
X
X    /* Generate a unique cache_name */
X    strcpy(cache_name, "MEMORY:");
X    (void) tmpnam(&cache_name[7]);
X
X    if ((krbret = krb5_cc_resolve(pam_context, cache_name, &ccache)) != 0) {
X       DLOG("krb5_cc_resolve()", error_message(krbret));
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup;
X    }
X    if ((krbret = krb5_cc_initialize(pam_context, ccache, princ)) != 0) {
X       DLOG("krb5_cc_initialize()", error_message(krbret));
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup;
X    }
X    if ((krbret = krb5_cc_store_cred(pam_context, ccache, &creds)) != 0) {
X       DLOG("krb5_cc_store_cred()", error_message(krbret));
X       (void) krb5_cc_destroy(pam_context, ccache);
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup;
X    }
X
X    /* Verify it */
X    if (verify_krb_v5_tgt(pam_context, ccache, service, debug) == -1) {
X       (void) krb5_cc_destroy(pam_context, ccache);
X       pamret = PAM_AUTH_ERR;
X       goto cleanup;
X    }
X
X    /* A successful authentication, store ccache for sm_setcred() */
X    if (!pam_get_data(pamh, "ccache", (const void **) &ccache_check)) {
X       DLOG("pam_get_data()", "ccache data already present");
X       (void) krb5_cc_destroy(pam_context, ccache);
X       pamret = PAM_AUTH_ERR;
X       goto cleanup;
X    }
X    if ((pamret = pam_set_data(pamh, "ccache", ccache, cleanup_cache)) != 0) {
X       DLOG("pam_set_data()", pam_strerror(pamh, pamret));
X       (void) krb5_cc_destroy(pam_context, ccache);
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup;
X    }
X
Xcleanup:
X    krb5_free_cred_contents(pam_context, &creds);
Xcleanup2:
X    krb5_free_principal(pam_context, princ);
Xcleanup3:
X    if (prompt)
X       free(prompt);
X    if (princ_name)
X       free(princ_name);
X
X    krb5_free_context(pam_context);
X    DLOG("exit", pamret ? "failure" : "success");
X    return pamret;
X}
X
X
X
X/* redefine this for pam_sm_setcred() */
X#undef DLOG
X#define DLOG(error_func, error_msg) \
Xif (debug) \
X    syslog(LOG_DEBUG, "pam_krb5: pam_sm_setcred(%s %s): %s: %s", \
X          service, name, error_func, error_msg)
X
X/* Called after a successful authentication. Set user credentials. */
Xint
Xpam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
X              const char **argv)
X{
X
X    krb5_error_code    krbret;
X    krb5_context       pam_context;
X    krb5_principal     princ;
X    krb5_creds         creds;
X    krb5_ccache                ccache_temp, ccache_perm;
X    krb5_cc_cursor     cursor;
X
X    int                        i, pamret;
X    char               *name, *service = NULL;
X    char               *cache_name = NULL, *cache_env_name;
X    struct passwd      *pw = NULL;
X
X    int                debug = 0;
X    uid_t      euid;
X    gid_t      egid;
X
X    if (flags == PAM_REINITIALIZE_CRED)
X       return PAM_SUCCESS; /* XXX Incorrect behavior */
X
X    if (flags != PAM_ESTABLISH_CRED)
X       return PAM_SERVICE_ERR;
X
X    for (i = 0; i < argc; i++) {
X       if (strcmp(argv[i], "debug") == 0)
X           debug = 1;
X       else if (strcmp(argv[i], "no_ccache") == 0)
X           return PAM_SUCCESS;
X       else if (strstr(argv[i], "ccache=") == argv[i])
X           cache_name = (char *) &argv[i][7]; /* save for later */
X    }
X
X    /* Get username */
X    if (pam_get_item(pamh, PAM_USER, (const void **) &name)) {
X       return PAM_SERVICE_ERR;
X    }
X
X    /* Get service name */
X    (void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
X    if (!service)
X       service = "unknown";
X
X    DLOG("entry", "");
X
X    if ((krbret = krb5_init_context(&pam_context)) != 0) {
X       DLOG("krb5_init_context()", error_message(krbret));
X       return PAM_SERVICE_ERR;
X    }
X
X    euid = geteuid(); /* Usually 0 */
X    egid = getegid();
X
X    /* Retrieve the cache name */
X    if ((pamret = pam_get_data(pamh, "ccache", (const void **) &ccache_temp)) 
X      != 0) {
X       DLOG("pam_get_data()", pam_strerror(pamh, pamret));
X       pamret = PAM_CRED_UNAVAIL;
X       goto cleanup3;
X    }
X
X    /* Get the uid. This should exist. */
X    pw = getpwnam(name);
X    if (!pw) {
X       DLOG("getpwnam()", name);
X       pamret = PAM_USER_UNKNOWN;
X       goto cleanup3;
X    }
X
X    /* Avoid following a symlink as root */
X    if (setegid(pw->pw_gid)) {
X       DLOG("setegid()", name); /* XXX should really log group name or id */
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup3;
X    }
X    if (seteuid(pw->pw_uid)) {
X       DLOG("seteuid()", name);
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup3;
X    }
X
X    /* Get the cache name */
X    if (!cache_name) {
X       cache_name = malloc(64); /* plenty big */
X       if (!cache_name) {
X           DLOG("malloc()", "failure");
X           pamret = PAM_BUF_ERR;
X           goto cleanup3;
X       }
X       sprintf(cache_name, "FILE:/tmp/krb5cc_%d", pw->pw_uid);
X    } else {
X       /* cache_name was supplied */
X       char *p = calloc(PATH_MAX + 10, 1); /* should be plenty */
X       char *q = cache_name;
X       if (!p) {
X           DLOG("malloc()", "failure");
X           pamret = PAM_BUF_ERR;
X           goto cleanup3;
X       }
X       cache_name = p;
X       
X       /* convert %u and %p */
X       while (*q) {
X           if (*q == '%') {
X               q++;
X               if (*q == 'u') {
X                   sprintf(p, "%d", pw->pw_uid);
X                   p += strlen(p);
X               } else if (*q == 'p') {
X                   sprintf(p, "%d", getpid());
X                   p += strlen(p);
X               } else {
X                   /* Not a special token */
X                   *p++ = '%';
X                   q--;
X               }
X               q++;
X           } else {
X               *p++ = *q++;
X           }
X       }
X    }
X
X    /* Initialize the new ccache */
X    if ((krbret = krb5_cc_get_principal(pam_context, ccache_temp, &princ)) 
X      != 0) {
X       DLOG("krb5_cc_get_principal()", error_message(krbret));
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup3;
X    }
X    if ((krbret = krb5_cc_resolve(pam_context, cache_name, &ccache_perm)) 
X      != 0) {
X       DLOG("krb5_cc_resolve()", error_message(krbret));
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup2;
X    }
X    if ((krbret = krb5_cc_initialize(pam_context, ccache_perm, princ)) != 0) {
X       DLOG("krb5_cc_initialize()", error_message(krbret));
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup2;
X    }
X
X    /* Prepare for iteration over creds */
X    if ((krbret = krb5_cc_start_seq_get(pam_context, ccache_temp, &cursor)) 
X      != 0) {
X       DLOG("krb5_cc_start_seq_get()", error_message(krbret));
X       (void) krb5_cc_destroy(pam_context, ccache_perm);
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup2;
X    }
X
X    /* Copy the creds (should be two of them) */
X    while ((krbret = compat_cc_next_cred(pam_context, ccache_temp,
X       &cursor, &creds) == 0)) {
X           if ((krbret = krb5_cc_store_cred(pam_context, ccache_perm, 
X               &creds)) != 0) {
X           DLOG("krb5_cc_store_cred()", error_message(krbret));
X           (void) krb5_cc_destroy(pam_context, ccache_perm);
X           krb5_free_cred_contents(pam_context, &creds);
X           pamret = PAM_SERVICE_ERR;
X           goto cleanup2;
X       }
X       krb5_free_cred_contents(pam_context, &creds);
X    }
X    (void) krb5_cc_end_seq_get(pam_context, ccache_temp, &cursor);
X
X    if (strstr(cache_name, "FILE:") == cache_name) {
X       if (chown(&cache_name[5], pw->pw_uid, pw->pw_gid) == -1) {
X           DLOG("chown()", strerror(errno));
X           (void) krb5_cc_destroy(pam_context, ccache_perm);
X           pamret = PAM_SERVICE_ERR;   
X           goto cleanup2;
X       }
X       if (chmod(&cache_name[5], (S_IRUSR|S_IWUSR)) == -1) {
X           DLOG("chmod()", strerror(errno));
X           (void) krb5_cc_destroy(pam_context, ccache_perm);
X           pamret = PAM_SERVICE_ERR;
X           goto cleanup2;
X       }
X    }
X    (void) krb5_cc_close(pam_context, ccache_perm);
X
X    cache_env_name = malloc(strlen(cache_name) + 12);
X    if (!cache_env_name) {
X       DLOG("malloc()", "failure");
X       (void) krb5_cc_destroy(pam_context, ccache_perm);
X       pamret = PAM_BUF_ERR;
X       goto cleanup2;
X    }
X
X    sprintf(cache_env_name, "KRB5CCNAME=%s", cache_name);
X    if ((pamret = pam_putenv(pamh, cache_env_name)) != 0) {
X       DLOG("pam_putenv()", pam_strerror(pamh, pamret));
X       (void) krb5_cc_destroy(pam_context, ccache_perm);
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup2;
X    }
X
Xcleanup2:
X    krb5_free_principal(pam_context, princ);
Xcleanup3:
X    krb5_free_context(pam_context);
X    DLOG("exit", pamret ? "failure" : "success");
X    (void) seteuid(euid);
X    (void) setegid(egid);
X    return pamret;
X}
X
END-of-lib/libpam/modules/pam_kerberos5/pam_krb5_auth.c
echo x - lib/libpam/modules/pam_kerberos5/pam_krb5_sess.c
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/pam_krb5_sess.c << 
'END-of-lib/libpam/modules/pam_kerberos5/pam_krb5_sess.c'
X/*
X * pam_krb5_sess.c
X *
X * PAM session management functions for pam_krb5
X * (null functions)
X *
X */
X
Xstatic const char rcsid[] = "$Id: pam_krb5_sess.c,v 1.3 1999/01/19 20:49:44 fcusack 
Exp $";
X
X#include <security/pam_appl.h>
X#include <security/pam_modules.h>
X
X/* Initiate session management */
Xint
Xpam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
X{
X    return PAM_SUCCESS;
X}
X
X
X/* Terminate session management */
Xint
Xpam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
X{
X    return PAM_SUCCESS;
X}
END-of-lib/libpam/modules/pam_kerberos5/pam_krb5_sess.c
echo x - lib/libpam/modules/pam_kerberos5/pam_krb5_pass.c
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/pam_krb5_pass.c << 
'END-of-lib/libpam/modules/pam_kerberos5/pam_krb5_pass.c'
X/*
X * pam_krb5_pass.c
X *
X * PAM password management functions for pam_krb5
X *
X */
X
Xstatic const char rcsid[] = "$Id: pam_krb5_pass.c,v 1.3 1999/01/19 23:43:11 fcusack 
Exp $";
X
X#include <errno.h>
X#include <stdio.h>     /* sprintf */
X#include <stdlib.h>    /* malloc */
X#include <syslog.h>    /* syslog */
X#include <security/pam_appl.h>
X#include <security/pam_modules.h>
X#include <krb5.h>
X#include <com_err.h>
X#include "pam_krb5.h"
X
X/* A useful logging macro */
X#define DLOG(error_func, error_msg) \
Xif (debug) \
X    syslog(LOG_DEBUG, "pam_krb5: pam_sm_chauthtok(%s %s): %s: %s", \
X          service, name, error_func, error_msg)
X
X/* Change a user's password */
Xint
Xpam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
X{
X    krb5_error_code    krbret;
X    krb5_context       pam_context;
X    krb5_creds         creds;
X    krb5_principal     princ;
X    krb5_get_init_creds_opt opts;
X
X    int                result_code;
X    krb5_data  result_code_string, result_string;
X
X    int                pamret, i;
X    char       *name, *service = NULL, *pass = NULL, *pass2;
X    char       *princ_name = NULL;
X    char       *prompt = NULL;
X
X    int debug = 0;
X    int try_first_pass = 0, use_first_pass = 0;
X
X    if (!(flags & PAM_UPDATE_AUTHTOK))
X       return PAM_AUTHTOK_ERR;
X
X    for (i = 0; i < argc; i++) {
X       if (strcmp(argv[i], "debug") == 0)
X           debug = 1;
X       else if (strcmp(argv[i], "try_first_pass") == 0)
X           try_first_pass = 1;
X       else if (strcmp(argv[i], "use_first_pass") == 0)
X           use_first_pass = 1;
X    }
X
X    /* Get username */
X    if ((pam_get_item(pamh, PAM_USER, (const void **) &name)) != 0) {
X       return PAM_SERVICE_ERR;
X    }
X
X    /* Get service name */
X    (void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
X    if (!service)
X       service = "unknown";
X
X    DLOG("entry", "");
X
X    if ((krbret = krb5_init_context(&pam_context)) != 0) {
X       DLOG("krb5_init_context()", error_message(krbret));
X       return PAM_SERVICE_ERR;
X    }
X
X    if ((krbret = krb5_init_context(&pam_context)) != 0) {
X       DLOG("krb5_init_context()", error_message(krbret));
X       return PAM_SERVICE_ERR;
X    }
X    krb5_get_init_creds_opt_init(&opts);
X    memset(&creds, 0, sizeof(krb5_creds));
X
X    /* Get principal name */
X    if ((krbret = krb5_parse_name(pam_context, name, &princ)) != 0) {
X       DLOG("krb5_parse_name()", error_message(krbret));
X       pamret = PAM_USER_UNKNOWN;
X       goto cleanup3;
X    }
X
X    /* Now convert the principal name into something human readable */
X    if ((krbret = krb5_unparse_name(pam_context, princ, &princ_name)) != 0) {
X       DLOG("krb5_unparse_name()", error_message(krbret));
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup2;
X    }
X
X    /* Get password */
X    prompt = malloc(16 + strlen(princ_name));
X    if (!prompt) {
X       DLOG("malloc()", "failure");
X       pamret = PAM_BUF_ERR;
X       goto cleanup2;
X    }
X    (void) sprintf(prompt, "Password for %s: ", princ_name);
X
X    if (try_first_pass || use_first_pass)
X       (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
X
Xget_pass:
X    if (!pass) {
X       try_first_pass = 0;
X       if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF, 
X         &pass)) != 0) {
X           DLOG("get_user_info()", pam_strerror(pamh, pamret));
X           pamret = PAM_SERVICE_ERR;
X           goto cleanup2;
X       }
X       /* We have to free pass. */
X       if ((pamret = pam_set_item(pamh, PAM_AUTHTOK, pass)) != 0) {
X           DLOG("pam_set_item()", pam_strerror(pamh, pamret));
X           free(pass);
X           pamret = PAM_SERVICE_ERR;
X           goto cleanup2;
X       }
X       free(pass);
X       /* Now we get it back from the library. */
X       (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
X    }
X
X    if ((krbret = krb5_get_init_creds_password(pam_context, &creds, princ, 
X      pass, pam_prompter, pamh, 0, "kadmin/changepw", &opts)) != 0) {
X       DLOG("krb5_get_init_creds_password()", error_message(krbret));
X       if (try_first_pass && krbret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
X           pass = NULL;
X           goto get_pass;
X       }
X       pamret = PAM_AUTH_ERR;
X       goto cleanup2;
X    }
X
X    /* Now get the new password */
X    free(prompt);
X    prompt = "Enter new password: ";
X    if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF, &pass)) 
X      != 0) {
X       DLOG("get_user_info()", pam_strerror(pamh, pamret));
X       prompt = NULL;
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup;
X    }
X    prompt = "Enter it again: ";
X    if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF, &pass2)) 
X      != 0) {
X       DLOG("get_user_info()", pam_strerror(pamh, pamret));
X       prompt = NULL;
X       pamret = PAM_SERVICE_ERR;
X       goto cleanup;
X    }
X    prompt = NULL;
X
X    if (strcmp(pass, pass2) != 0) {
X       DLOG("strcmp()", "passwords not equal");
X        pamret = PAM_AUTHTOK_ERR;
X       goto cleanup;
X    }
X
X    /* Change it */
X    if ((krbret = krb5_change_password(pam_context, &creds, pass,
X      &result_code, &result_code_string, &result_string)) != 0) {
X       DLOG("krb5_change_password()", error_message(krbret));
X       pamret = PAM_AUTHTOK_ERR;
X       goto cleanup;
X    }
X    if (result_code) {
X       DLOG("krb5_change_password() (result_code)", "");
X       pamret = PAM_AUTHTOK_ERR;
X       goto cleanup;
X    }
X
X    if (result_string.data)
X       free(result_string.data);
X    if (result_code_string.data)
X       free(result_code_string.data);
X
Xcleanup:
X    krb5_free_cred_contents(pam_context, &creds);
Xcleanup2:
X    krb5_free_principal(pam_context, princ);
Xcleanup3:
X    if (prompt)
X       free(prompt);
X    if (princ_name)
X       free(princ_name);
X
X    krb5_free_context(pam_context);
X    DLOG("exit", pamret ? "failure" : "success");
X    return pamret;
X}
X
END-of-lib/libpam/modules/pam_kerberos5/pam_krb5_pass.c
echo x - lib/libpam/modules/pam_kerberos5/support.c
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/support.c << 
'END-of-lib/libpam/modules/pam_kerberos5/support.c'
X/*
X * support.c
X *
X * Support functions for pam_krb5
X */
X
Xstatic const char rcsid[] = "$Id: support.c,v 1.8 2000/01/04 09:50:03 fcusack Exp $";
X
X#include <errno.h>
X#include <stdio.h>     /* BUFSIZ */
X#include <stdlib.h>    /* malloc */
X#include <string.h>    /* strncpy */
X#include <syslog.h>    /* syslog */
X#include <security/pam_appl.h>
X#include <security/pam_modules.h>
X#include <krb5.h>
X#include <com_err.h>
X#include "pam_krb5.h"
X
X/*
X * Get info from the user. Disallow null responses (regardless of flags).
X * response gets allocated and filled in on successful return. Caller
X * is responsible for freeing it.
X */
Xint
Xget_user_info(pam_handle_t *pamh, char *prompt, int type, char **response)
X{
X    int pamret;
X    struct pam_message msg;
X    const struct pam_message *pmsg;
X    struct pam_response        *resp = NULL;
X    struct pam_conv    *conv;
X
X    if ((pamret = pam_get_item(pamh, PAM_CONV, (const void **) &conv)) != 0)
X       return pamret;
X
X    /* set up conversation call */
X    pmsg = &msg;
X    msg.msg_style = type;
X    msg.msg = prompt;
X
X    if ((pamret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr)) != 0)
X       return pamret;
X
X    /* Caller should ignore errors for non-response conversations */
X    if (!resp)
X       return PAM_CONV_ERR;
X
X    if (!(resp->resp && resp->resp[0])) {
X       free(resp);
X       return PAM_AUTH_ERR;
X    }
X
X    *response = resp->resp;
X    free(resp);
X    return pamret;
X}
X
X/*
X * This routine with some modification is from the MIT V5B6 appl/bsd/login.c
X * Modified by Sam Hartman <[EMAIL PROTECTED]> to support PAM services
X * for Debian.
X *
X * Verify the Kerberos ticket-granting ticket just retrieved for the
X * user.  If the Kerberos server doesn't respond, assume the user is
X * trying to fake us out (since we DID just get a TGT from what is
X * supposedly our KDC).  If the host/<host> service is unknown (i.e.,
X * the local keytab doesn't have it), and we cannot find another
X * service we do have, let her in.
X *
X * Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
X */
Xint
Xverify_krb_v5_tgt(krb5_context context, krb5_ccache ccache,
X                 char * pam_service, int debug)
X{
X    char               phost[BUFSIZ];
X    char *services [3];
X    char **service;
X    krb5_error_code    retval = -1;
X    krb5_principal     princ;
X    krb5_keyblock *    keyblock = 0;
X    krb5_data          packet;
X    krb5_auth_context  auth_context = NULL;
X
X    packet.data = 0;
X
X    /*
X    * If possible we want to try and verify the ticket we have
X    * received against a keytab.  We will try multiple service
X    * principals, including at least the host principal and the PAM
X    * service principal.  The host principal is preferred because access
X    * to that key is generally sufficient to compromise root, while the
X    *     service key for this PAM service may be less carefully guarded.
X    * It is important to check the keytab first before the KDC so we do
X    * not get spoofed by a fake  KDC.*/
X    services [0] = "host";
X    services [1] = pam_service;
X    services [2] = NULL;
X    for ( service = &services[0]; *service != NULL; service++ ) {
X      if ((retval = krb5_sname_to_principal(context, NULL, *service, KRB5_NT_SRV_HST,
X                                           &princ)) != 0) {
X       if (debug)
X         syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
X                "krb5_sname_to_principal()", error_message(retval));
X       return -1;
X      }
X
X      /* Extract the name directly. */
X      strncpy(phost, compat_princ_component(context, princ, 1), BUFSIZ);
X      phost[BUFSIZ - 1] = '\0';
X
X      /*
X       * Do we have service/<host> keys?
X       * (use default/configured keytab, kvno IGNORE_VNO to get the
X       * first match, and ignore enctype.)
X       */
X      if ((retval = krb5_kt_read_service_key(context, NULL, princ, 0,
X                                            0, &keyblock)) != 0)
X       continue;
X      break;
X    }
X    if (retval != 0 ) {                /* failed to find key */
X       /* Keytab or service key does not exist */
X       if (debug)
X           syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
X                  "krb5_kt_read_service_key()", error_message(retval));
X       retval = 0;
X       goto cleanup;
X    }
X    if (keyblock)
X       krb5_free_keyblock(context, keyblock);
X
X    /* Talk to the kdc and construct the ticket. */
X    retval = krb5_mk_req(context, &auth_context, 0, *service, phost,
X                        NULL, ccache, &packet);
X    if (auth_context) {
X       krb5_auth_con_free(context, auth_context);
X       auth_context = NULL; /* setup for rd_req */
X    }
X    if (retval) {
X       if (debug)
X           syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
X                  "krb5_mk_req()", error_message(retval));
X       retval = -1;
X       goto cleanup;
X    }
X
X    /* Try to use the ticket. */
X    retval = krb5_rd_req(context, &auth_context, &packet, princ,
X                        NULL, NULL, NULL);
X    if (retval) {
X       if (debug)
X           syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
X                  "krb5_rd_req()", error_message(retval));
X       retval = -1;
X    } else {
X       retval = 1;
X    }
X
Xcleanup:
X    if (packet.data)
X       compat_free_data_contents(context, &packet);
X    krb5_free_principal(context, princ);
X    return retval;
X
X}
X
X
X/* Free the memory for cache_name. Called by pam_end() */
Xvoid
Xcleanup_cache(pam_handle_t *pamh, void *data, int pam_end_status)
X{
X    krb5_context       pam_context;
X    krb5_ccache                ccache;
X
X    if (krb5_init_context(&pam_context))
X       return;
X
X    ccache = (krb5_ccache) data;
X    (void) krb5_cc_destroy(pam_context, ccache);
X    krb5_free_context(pam_context);
X}
END-of-lib/libpam/modules/pam_kerberos5/support.c
echo x - lib/libpam/modules/pam_kerberos5/pam_krb5_acct.c
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/pam_krb5_acct.c << 
'END-of-lib/libpam/modules/pam_kerberos5/pam_krb5_acct.c'
X/*
X * pam_krb5_acct.c
X *
X * PAM account management functions for pam_krb5
X *
X */
X
Xstatic const char rcsid[] = "$Id: pam_krb5_acct.c,v 1.3 1999/01/19 21:26:44 fcusack 
Exp $";
X
X#include <syslog.h>    /* syslog */
X#include <security/pam_appl.h>
X#include <security/pam_modules.h>
X#include <krb5.h>
X#include <com_err.h>
X#include "pam_krb5.h"
X
X/* A useful logging macro */
X#define DLOG(error_func, error_msg) \
Xif (debug) \
X    syslog(LOG_DEBUG, "pam_krb5: pam_sm_acct_mgmt(%s %s): %s: %s", \
X          service, name, error_func, error_msg)
X
X/* Check authorization of user */
Xint
Xpam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
X{
X    krb5_error_code    krbret;
X    krb5_context       pam_context;
X    krb5_ccache                ccache;
X    krb5_principal     princ;
X
X    char               *service, *name;
X    int                        debug = 0;
X    int                        i, pamret;
X
X    for (i = 0; i < argc; i++) {
X       if (strcmp(argv[i], "debug") == 0)
X           debug = 1;
X    }
X
X    /* Get username */
X    if (pam_get_item(pamh, PAM_USER, (const void **) &name)) {
X       return PAM_PERM_DENIED;;
X    }
X
X    /* Get service name */
X    (void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
X    if (!service)
X       service = "unknown";
X
X    DLOG("entry", "");
X
X    if (pam_get_data(pamh, "ccache", (const void **) &ccache)) {
X       /* User did not use krb5 to login */
X       DLOG("ccache", "not found");
X       return PAM_SUCCESS;
X    }
X
X    if ((krbret = krb5_init_context(&pam_context)) != 0) {
X       DLOG("krb5_init_context()", error_message(krbret));
X       return PAM_PERM_DENIED;;
X    }
X
X    if ((krbret = krb5_cc_get_principal(pam_context, ccache, &princ)) != 0) {
X       DLOG("krb5_cc_get_principal()", error_message(krbret));
X       pamret = PAM_PERM_DENIED;;
X       goto cleanup;
X    }
X
X    if (krb5_kuserok(pam_context, princ, name))
X       pamret = PAM_SUCCESS;
X    else
X       pamret = PAM_PERM_DENIED;
X    krb5_free_principal(pam_context, princ);
X
Xcleanup:
X    krb5_free_context(pam_context);
X    DLOG("exit", pamret ? "failure" : "success");
X    return pamret;
X
X}
X
END-of-lib/libpam/modules/pam_kerberos5/pam_krb5_acct.c
echo x - lib/libpam/modules/pam_kerberos5/compat_mit.c
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/compat_mit.c << 
'END-of-lib/libpam/modules/pam_kerberos5/compat_mit.c'
X#include <errno.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X
X#include <krb5.h>
X#include <security/pam_appl.h>
X#include <security/pam_modules.h>
X#include "pam_krb5.h"
X
Xconst char *
Xcompat_princ_component(krb5_context context, krb5_principal princ, int n)
X{
X       return krb5_princ_component(context, princ, n)->data;
X}
X
Xvoid
Xcompat_free_data_contents(krb5_context context, krb5_data *data)
X{
X       krb5_free_data_contents(context, data);
X}
X
Xkrb5_error_code
Xcompat_cc_next_cred(krb5_context context, const krb5_ccache id, 
X    krb5_cc_cursor *cursor, krb5_creds *creds)
X{
X       return krb5_cc_next_cred(context, id, cursor, creds);
X}
X
Xstatic krb5_error_code
Xmit_pam_prompter(krb5_context context, void *data, const char *name,
X            const char *banner, int num_prompts, krb5_prompt prompts[])
X{
X    int                pam_prompts = num_prompts;
X    int                pamret, i;
X
X    struct pam_message *msg;
X    struct pam_response        *resp = NULL;
X    struct pam_conv    *conv;
X    pam_handle_t       *pamh = (pam_handle_t *) data;
X
X    if ((pamret = pam_get_item(pamh, PAM_CONV, (const void **) &conv)) != 0)
X       return KRB5KRB_ERR_GENERIC;
X
X    if (name)
X       pam_prompts++;
X
X    if (banner)
X       pam_prompts++;
X
X    msg = calloc(sizeof(struct pam_message) * pam_prompts, 1);
X    if (!msg)
X       return ENOMEM;
X
X    /* Now use pam_prompts as an index */
X    pam_prompts = 0;
X
X    /* Sigh. malloc all the prompts. */
X    if (name) {
X       msg[pam_prompts].msg = malloc(strlen(name) + 1);
X       if (!msg[pam_prompts].msg)
X           goto cleanup;
X       strcpy((char *) msg[pam_prompts].msg, name);
X       msg[pam_prompts].msg_style = PAM_TEXT_INFO;
X       pam_prompts++;
X    }
X
X    if (banner) {
X       msg[pam_prompts].msg = malloc(strlen(banner) + 1);
X       if (!msg[pam_prompts].msg)
X           goto cleanup;
X       strcpy((char *) msg[pam_prompts].msg, banner);
X       msg[pam_prompts].msg_style = PAM_TEXT_INFO;
X       pam_prompts++;
X    }
X
X    for (i = 0; i < num_prompts; i++) {
X       msg[pam_prompts].msg = malloc(strlen(prompts[i].prompt) + 3);
X       if (!msg[pam_prompts].msg)
X           goto cleanup;
X       sprintf((char *) msg[pam_prompts].msg, "%s: ", prompts[i].prompt);
X       msg[pam_prompts].msg_style = prompts[i].hidden ? PAM_PROMPT_ECHO_OFF
X                                                      : PAM_PROMPT_ECHO_ON;
X       pam_prompts++;
X    }
X
X    if ((pamret = conv->conv(pam_prompts, (const struct pam_message **) &msg, 
X      &resp, conv->appdata_ptr)) != 0)
X       goto cleanup;
X
X    if (!resp)
X       goto cleanup;
X
X    /* Reuse pam_prompts as a starting index */
X    pam_prompts = 0;
X    if (name)
X       pam_prompts++;
X    if (banner)
X       pam_prompts++;
X
X    for (i = 0; i < num_prompts; i++, pam_prompts++) {
X       register int len;
X       if (!resp[pam_prompts].resp) {
X           pamret = PAM_AUTH_ERR;
X           goto cleanup;
X       }
X       len = strlen(resp[pam_prompts].resp); /* Help out the compiler */
X       if (len > prompts[i].reply->length) {
X           pamret = PAM_AUTH_ERR;
X           goto cleanup;
X       }
X       memcpy(prompts[i].reply->data, resp[pam_prompts].resp, len);
X       prompts[i].reply->length = len;
X    }
X
Xcleanup:
X    /* pam_prompts is correct at this point */
X
X    for (i = 0; i < pam_prompts; i++) {
X       if (msg[i].msg)
X           free((char *) msg[i].msg);
X    }
X    free(msg);
X
X    if (resp) {
X       for (i = 0; i < pam_prompts; i++) {
X           /*
X            * Note that PAM is underspecified wrt free()'ing resp[i].resp.
X            * It's not clear if I should free it, or if the application
X            * has to. Therefore most (all?) apps won't free() it, and I
X            * can't either, as I am not sure it was malloc()'d. All PAM
X            * implementations I've seen leak memory here. Not so bad, IFF
X            * you fork/exec for each PAM authentication (as is typical).
X            */
X#if 0
X           if (resp[i].resp)
X               free(resp[i].resp);
X#endif /* 0 */
X       }
X       /* This does not lose resp[i].resp if the application saved a copy. */
X       free(resp);
X    }
X
X    return (pamret ? KRB5KRB_ERR_GENERIC : 0);
X}
X
Xkrb5_prompter_fct pam_prompter = mit_pam_prompter;
END-of-lib/libpam/modules/pam_kerberos5/compat_mit.c
echo x - lib/libpam/modules/pam_kerberos5/compat_heimdal.c
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/compat_heimdal.c << 
'END-of-lib/libpam/modules/pam_kerberos5/compat_heimdal.c'
X#include <errno.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X
X#include <krb5.h>
X#include <security/pam_appl.h>
X#include <security/pam_modules.h>
X#include "pam_krb5.h"
X
Xconst char *
Xcompat_princ_component(krb5_context context, krb5_principal princ, int n)
X{
X       return princ->name.name_string.val[n];
X}
X
Xvoid
Xcompat_free_data_contents(krb5_context context, krb5_data *data)
X{
X       krb5_xfree(data->data);
X}
X
Xkrb5_error_code
Xcompat_cc_next_cred(krb5_context context, const krb5_ccache id, 
X    krb5_cc_cursor *cursor, krb5_creds *creds)
X{
X       return krb5_cc_next_cred(context, id, creds, cursor);
X}
X
X
Xstatic krb5_error_code
Xheimdal_pam_prompter(krb5_context context, void *data, const char *banner, int 
X  num_prompts, krb5_prompt prompts[])
X{
X    int                pam_prompts = num_prompts;
X    int                pamret, i;
X
X    struct pam_message *msg;
X    struct pam_response        *resp = NULL;
X    struct pam_conv    *conv;
X    pam_handle_t       *pamh = (pam_handle_t *) data;
X
X    if ((pamret = pam_get_item(pamh, PAM_CONV, (const void **) &conv)) != 0)
X       return KRB5KRB_ERR_GENERIC;
X
X    if (banner)
X       pam_prompts++;
X
X    msg = calloc(sizeof(struct pam_message) * pam_prompts, 1);
X    if (!msg)
X       return ENOMEM;
X
X    /* Now use pam_prompts as an index */
X    pam_prompts = 0;
X
X    if (banner) {
X       msg[pam_prompts].msg = malloc(strlen(banner) + 1);
X       if (!msg[pam_prompts].msg)
X           goto cleanup;
X       strcpy((char *) msg[pam_prompts].msg, banner);
X       msg[pam_prompts].msg_style = PAM_TEXT_INFO;
X       pam_prompts++;
X    }
X
X    for (i = 0; i < num_prompts; i++) {
X       msg[pam_prompts].msg = malloc(strlen(prompts[i].prompt) + 3);
X       if (!msg[pam_prompts].msg)
X           goto cleanup;
X       sprintf((char *) msg[pam_prompts].msg, "%s: ", prompts[i].prompt);
X       msg[pam_prompts].msg_style = prompts[i].hidden ? PAM_PROMPT_ECHO_OFF
X                                                      : PAM_PROMPT_ECHO_ON;
X       pam_prompts++;
X    }
X
X    if ((pamret = conv->conv(pam_prompts, (const struct pam_message **) &msg, 
X      &resp, conv->appdata_ptr)) != 0) 
X       goto cleanup;
X
X    if (!resp)
X       goto cleanup;
X
X    /* Reuse pam_prompts as a starting index */
X    pam_prompts = 0;
X    if (banner)
X       pam_prompts++;
X
X    for (i = 0; i < num_prompts; i++, pam_prompts++) {
X       register int len;
X       if (!resp[pam_prompts].resp) {
X           pamret = PAM_AUTH_ERR;
X           goto cleanup;
X       }
X       len = strlen(resp[pam_prompts].resp); /* Help out the compiler */
X       if (len > prompts[i].reply->length) {
X           pamret = PAM_AUTH_ERR;
X           goto cleanup;
X       }
X       memcpy(prompts[i].reply->data, resp[pam_prompts].resp, len);
X       prompts[i].reply->length = len;
X    }
X
Xcleanup:
X    /* pam_prompts is correct at this point */
X
X    for (i = 0; i < pam_prompts; i++) {
X       if (msg[i].msg)
X           free((char *) msg[i].msg);
X    }
X    free(msg);
X
X    if (resp) {
X       for (i = 0; i < pam_prompts; i++) {
X           /*
X            * Note that PAM is underspecified wrt free()'ing resp[i].resp.
X            * It's not clear if I should free it, or if the application
X            * has to. Therefore most (all?) apps won't free() it, and I
X            * can't either, as I am not sure it was malloc()'d. All PAM
X            * implementations I've seen leak memory here. Not so bad, IFF
X            * you fork/exec for each PAM authentication (as is typical).
X            */
X#if 0
X           if (resp[i].resp)
X               free(resp[i].resp);
X#endif /* 0 */
X       }
X       /* This does not lose resp[i].resp if the application saved a copy. */
X       free(resp);
X    }
X
X    return (pamret ? KRB5KRB_ERR_GENERIC : 0);
X}
X
Xkrb5_prompter_fct pam_prompter = heimdal_pam_prompter;
END-of-lib/libpam/modules/pam_kerberos5/compat_heimdal.c
echo x - lib/libpam/modules/pam_kerberos5/pam_krb5.h
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/pam_krb5.h << 
'END-of-lib/libpam/modules/pam_kerberos5/pam_krb5.h'
X/*
X * pam_krb5.h
X *
X * $Id: pam_krb5.h,v 1.5 1999/01/19 23:43:10 fcusack Exp $
X */
X
Xint get_user_info(pam_handle_t *, char *, int, char **);
Xint verify_krb_v5_tgt(krb5_context, krb5_ccache, char *, int);
Xvoid cleanup_cache(pam_handle_t *, void *, int);
X
Xkrb5_prompter_fct pam_prompter;
X
Xconst char     *compat_princ_component(krb5_context, krb5_principal, int);
Xvoid            compat_free_data_contents(krb5_context, krb5_data *);
Xkrb5_error_code         compat_cc_next_cred(krb5_context, const krb5_ccache, 
X                                    krb5_cc_cursor *, krb5_creds *);
X
X#ifndef ENCTYPE_DES_CBC_MD5
X#define ENCTYPE_DES_CBC_MD5    ETYPE_DES_CBC_MD5
X#endif
X
X
END-of-lib/libpam/modules/pam_kerberos5/pam_krb5.h
echo x - lib/libpam/modules/pam_kerberos5/COPYRIGHT
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/COPYRIGHT << 
'END-of-lib/libpam/modules/pam_kerberos5/COPYRIGHT'
Xpam_krb5:
X
XCopyright (c) Frank Cusack, 1999-2000.
[EMAIL PROTECTED]
XAll rights reserved
X
XRedistribution and use in source and binary forms, with or without
Xmodification, are permitted provided that the following conditions
Xare met:
X1. Redistributions of source code must retain the above copyright
X   notice, and the entire permission notice in its entirety,
X   including the disclaimer of warranties.
X2. Redistributions in binary form must reproduce the above copyright
X   notice, this list of conditions and the following disclaimer in the
X   documentation and/or other materials provided with the distribution.
X3. The name of the author may not be used to endorse or promote
X   products derived from this software without specific prior
X   written permission.
X
XALTERNATIVELY, this product may be distributed under the terms of
Xthe GNU Public License, in which case the provisions of the GPL are
Xrequired INSTEAD OF the above restrictions.  (This clause is
Xnecessary due to a potential bad interaction between the GPL and
Xthe restrictions contained in a BSD-style copyright.)
X
XTHIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
XWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
XOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
XDISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
XINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
XSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
XHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
XSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
XARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
XOF THE POSSIBILITY OF SUCH DAMAGE.
X
X---------------------------------------------------------------------------
X
XThis software may contain code from Naomaru Itoi:
X
XPAM-kerberos5 module Copyright notice.
XNaomaru Itoi <[EMAIL PROTECTED]>, June 24, 1997.
X
X----------------------------------------------------------------------------
XCOPYRIGHT (c)  1997
XTHE REGENTS OF THE UNIVERSITY OF MICHIGAN
XALL RIGHTS RESERVED
X
XPERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS AND REDISTRIBUTE
XTHIS SOFTWARE AND SUCH DERIVATIVE WORKS FOR ANY PURPOSE, SO LONG AS THE NAME
XOF THE UNIVERSITY OF MICHIGAN IS NOT USED IN ANY ADVERTISING OR PUBLICITY
XPERTAINING TO THE USE OR DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC,
XWRITTEN PRIOR AUTHORIZATION.  IF THE ABOVE COPYRIGHT NOTICE OR ANY OTHER
XIDENTIFICATION OF THE UNIVERSITY OF MICHIGAN IS INCLUDED IN ANY COPY OF ANY
XPORTION OF THIS SOFTWARE, THEN THE DISCLAIMER BELOW MUST ALSO BE INCLUDED.
X
XTHE SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY OF
XMICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE
XUNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
XWITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABITILY AND FITNESS FOR A
XPARTICULAR PURPOSE.  THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
XLIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
XCONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN
XCONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER
XADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
X
XPAM-kerberos5 module is written based on PAM-kerberos4 module
Xby Derrick J. Brashear and kerberos5-1.0pl1 by M.I.T. kerberos team.
XPermission to use, copy, modify, distribute this software is hereby
Xgranted, as long as it is granted by Derrick J. Brashear and
XM.I.T. kerberos team. Followings are their copyright information.  
X----------------------------------------------------------------------------
X
XThis software may contain code from Derrick J. Brashear:
X
X
XCopyright (c) Derrick J. Brashear, 1996. All rights reserved
X
XRedistribution and use in source and binary forms, with or without
Xmodification, are permitted provided that the following conditions
Xare met:
X1. Redistributions of source code must retain the above copyright
X   notice, and the entire permission notice in its entirety,
X   including the disclaimer of warranties.
X2. Redistributions in binary form must reproduce the above copyright
X   notice, this list of conditions and the following disclaimer in the
X   documentation and/or other materials provided with the distribution.
X3. The name of the author may not be used to endorse or promote
X   products derived from this software without specific prior
X   written permission.
X
XALTERNATIVELY, this product may be distributed under the terms of
Xthe GNU Public License, in which case the provisions of the GPL are
Xrequired INSTEAD OF the above restrictions.  (This clause is
Xnecessary due to a potential bad interaction between the GPL and
Xthe restrictions contained in a BSD-style copyright.)
X
XTHIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
XWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
XOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
XDISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
XINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
XSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
XHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
XSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
XARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
XOF THE POSSIBILITY OF SUCH DAMAGE.
X
X----------------------------------------------------------------------------
X
XThis software may contain code from MIT Kerberos 5:
X
XCopyright Notice and Legal Administrivia
X----------------------------------------
X
XCopyright (C) 1996 by the Massachusetts Institute of Technology.
X
XAll rights reserved.
X
XExport of this software from the United States of America may require
Xa specific license from the United States Government.  It is the
Xresponsibility of any person or organization contemplating export to
Xobtain such a license before exporting.
X
XWITHIN THAT CONSTRAINT, permission to use, copy, modify, and
Xdistribute this software and its documentation for any purpose and
Xwithout fee is hereby granted, provided that the above copyright
Xnotice appear in all copies and that both that copyright notice and
Xthis permission notice appear in supporting documentation, and that
Xthe name of M.I.T. not be used in advertising or publicity pertaining
Xto distribution of the software without specific, written prior
Xpermission.  M.I.T. makes no representations about the suitability of
Xthis software for any purpose.  It is provided "as is" without express
Xor implied warranty.
X
XTHIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
XIMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
XWARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X
XIndividual source code files are copyright MIT, Cygnus Support,
XOpenVision, Oracle, Sun Soft, and others.
X
XProject Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira,
Xand Zephyr are trademarks of the Massachusetts Institute of Technology
X(MIT).  No commercial use of these trademarks may be made without
Xprior written permission of MIT.
X
X"Commercial use" means use of a name in a product or other for-profit
Xmanner.  It does NOT prevent a commercial firm from referring to the
XMIT trademarks in order to convey information (although in doing so,
Xrecognition of their trademark status should be given).
X
XThe following copyright and permission notice applies to the
XOpenVision Kerberos Administration system located in kadmin/create,
Xkadmin/dbutil, kadmin/passwd, kadmin/server, lib/kadm5, and portions
Xof lib/rpc:
X
X   Copyright, OpenVision Technologies, Inc., 1996, All Rights Reserved
X
X   WARNING: Retrieving the OpenVision Kerberos Administration system 
X   source code, as described below, indicates your acceptance of the 
X   following terms.  If you do not agree to the following terms, do not 
X   retrieve the OpenVision Kerberos administration system.
X
X   You may freely use and distribute the Source Code and Object Code
X   compiled from it, with or without modification, but this Source
X   Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY,
X   INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR
X   FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER
X   EXPRESS OR IMPLIED.  IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY
X   FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF 
X   SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR
X   CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING, 
X   WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE 
X   CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY 
X   OTHER REASON.
X
X   OpenVision retains all copyrights in the donated Source Code. OpenVision
X   also retains copyright to derivative works of the Source Code, whether
X   created by OpenVision or by a third party. The OpenVision copyright 
X   notice must be preserved if derivative works are made based on the 
X   donated Source Code.
X
X   OpenVision Technologies, Inc. has donated this Kerberos 
X   Administration system to MIT for inclusion in the standard 
X   Kerberos 5 distribution.  This donation underscores our 
X   commitment to continuing Kerberos technology development 
X   and our gratitude for the valuable work which has been 
X   performed by MIT and the Kerberos community.
X
X
END-of-lib/libpam/modules/pam_kerberos5/COPYRIGHT
echo x - lib/libpam/modules/pam_kerberos5/README.fcusack
sed 's/^X//' >lib/libpam/modules/pam_kerberos5/README.fcusack << 
'END-of-lib/libpam/modules/pam_kerberos5/README.fcusack'
XThis is the README for pam_krb5, a PAM module which support Kerberos 5
Xauthentication.
X
XThis software is Copyright (c) 1999-2000 Frank Cusack.
XAll Rights Reserved.
X
XSee the COPYRIGHT file, included with this distribution, for copyright
Xand redistribution information.
X
XAuthor:
XFrank Cusack
X<[EMAIL PROTECTED]>
X
X
XI. Kerberos notes
X
XThis PAM module requires the MIT 1.1+ release of Kerberos, or the Cygnus
XCNS distribution. It has not been tested against heimdal or any other
XKerberos distributions.
X
XUnlike other PAM Kerberos 5 modules out there, this one does not
Xuse any private Kerberos interfaces. Thus, you need only the
Xheader files and libraries that are part of the Kerberos distribution.
X
X
XII. OS notes
X
XThis software has been tested against Solaris 2.6. It should compile
Xagainst Linux (distributions?) with minimal (if any) changes. Reports
Xof OS [in]compatibilities are welcomed.
X
Xdtlogin on Solaris doesn't support xrealm logins (probably a good thing).
X
XIII. PAM notes/open issues
X
Xauth module:
XWhen is pam_sm_setcred() ever called with flags other than PAM_ESTABLISH_CRED?
XIt would be fairly easy to support PAM_DELETE_CRED.
X
Xacct module:
XI believe this to be complete.
X
Xsession module:
XThis is complete (both functions just return success).
X
Xpasswd module:
XWhen is pam_sm_chauthtok() ever called with flags other than
XPAM_UPDATE_AUTHTOK?
X
X
XIV. Usage
X
XSimply change /etc/pam.conf to include this module. Make sure to include
Xthe acct category whenever you use the auth category, or .k5login will
Xnot get checked.
X
XYou probably want to make this module "sufficient", before your unix
X(or other) module(s).
X
X
XV. Acknowledgements
X
XThanks to Naomaru Itoi <[EMAIL PROTECTED]>,
XCurtis King <[EMAIL PROTECTED]>, and Derrick Brashear <[EMAIL PROTECTED]>,
Xall of whom have written and made available Kerberos 4/5 modules.
XAlthough no code in this module is directly from these author's modules,
X(except the get_user_info() routine in support.c; derived from whichever
Xof these authors originally wrote the first module the other 2 copied from),
Xit was extremely helpful to look over their code which aided in my design.
X
END-of-lib/libpam/modules/pam_kerberos5/README.fcusack
exit


To Unsubscribe: send mail to [EMAIL PROTECTED]
with "unsubscribe freebsd-hackers" in the body of the message

Reply via email to