--- support.c.orig	Tue Jan  4 19:08:51 2000
+++ support.c	Wed Jan 24 13:37:28 2001
@@ -6,11 +6,15 @@
 
 static const char rcsid[] = "$Id: support.c,v 1.8 2000/01/04 09:50:03 fcusack Exp $";
 
+#include <errno.h>
 #include <stdio.h>	/* BUFSIZ */
+#include <stdlib.h>	/* malloc */
+#include <string.h>	/* strncpy */
 #include <syslog.h>	/* syslog */
 #include <security/pam_appl.h>
 #include <security/pam_modules.h>
 #include <krb5.h>
+#include <com_err.h>
 #include "pam_krb5.h"
 
 /*
@@ -22,11 +26,12 @@
 get_user_info(pam_handle_t *pamh, char *prompt, int type, char **response)
 {
     int pamret;
-    struct pam_message	msg, *pmsg;
+    struct pam_message	msg;
+    const struct pam_message *pmsg;
     struct pam_response	*resp = NULL;
     struct pam_conv	*conv;
 
-    if (pamret = pam_get_item(pamh, PAM_CONV, (void **) &conv))
+    if ((pamret = pam_get_item(pamh, PAM_CONV, (const void **) &conv)) != 0)
 	return pamret;
 
     /* set up conversation call */
@@ -34,7 +39,7 @@
     msg.msg_style = type;
     msg.msg = prompt;
 
-    if (pamret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr))
+    if ((pamret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr)) != 0)
 	return pamret;
 
     /* Caller should ignore errors for non-response conversations */
@@ -51,172 +56,71 @@
     return pamret;
 }
 
-
-krb5_error_code
-pam_prompter(krb5_context context, void *data, const char *name,
-	     const char *banner, int num_prompts, krb5_prompt prompts[])
-{
-    int		pam_prompts = num_prompts;
-    int		pamret, i;
-
-    struct pam_message	*msg;
-    struct pam_response	*resp = NULL;
-    struct pam_conv	*conv;
-    pam_handle_t	*pamh = (pam_handle_t *) data;
-
-    if (pamret = pam_get_item(pamh, PAM_CONV, (void **) &conv))
-	return KRB5KRB_ERR_GENERIC;
-
-    if (name)
-	pam_prompts++;
-
-    if (banner)
-	pam_prompts++;
-
-    msg = calloc(sizeof(struct pam_message) * pam_prompts, 1);
-    if (!msg)
-	return ENOMEM;
-
-    /* Now use pam_prompts as an index */
-    pam_prompts = 0;
-
-    /* Sigh. malloc all the prompts. */
-    if (name) {
-	msg[pam_prompts].msg = malloc(strlen(name) + 1);
-	if (!msg[pam_prompts].msg)
-	    goto cleanup;
-	strcpy(msg[pam_prompts].msg, name);
-	msg[pam_prompts].msg_style = PAM_TEXT_INFO;
-	pam_prompts++;
-    }
-
-    if (banner) {
-	msg[pam_prompts].msg = malloc(strlen(banner) + 1);
-	if (!msg[pam_prompts].msg)
-	    goto cleanup;
-	strcpy(msg[pam_prompts].msg, banner);
-	msg[pam_prompts].msg_style = PAM_TEXT_INFO;
-	pam_prompts++;
-    }
-
-    for (i = 0; i < num_prompts; i++) {
-	msg[pam_prompts].msg = malloc(strlen(prompts[i].prompt) + 3);
-	if (!msg[pam_prompts].msg)
-	    goto cleanup;
-	sprintf(msg[pam_prompts].msg, "%s: ", prompts[i].prompt);
-	msg[pam_prompts].msg_style = prompts[i].hidden ? PAM_PROMPT_ECHO_OFF
-						       : PAM_PROMPT_ECHO_ON;
-	pam_prompts++;
-    }
-
-    if (pamret = conv->conv(pam_prompts, &msg, &resp, conv->appdata_ptr))
-	goto cleanup;
-
-    if (!resp)
-	goto cleanup;
-
-    /* Reuse pam_prompts as a starting index */
-    pam_prompts = 0;
-    if (name)
-	pam_prompts++;
-    if (banner)
-	pam_prompts++;
-
-    for (i = 0; i < num_prompts; i++, pam_prompts++) {
-	register int len;
-	if (!resp[pam_prompts].resp) {
-	    pamret = PAM_AUTH_ERR;
-	    goto cleanup;
-	}
-	len = strlen(resp[pam_prompts].resp); /* Help out the compiler */
-	if (len > prompts[i].reply->length) {
-	    pamret = PAM_AUTH_ERR;
-	    goto cleanup;
-	}
-	memcpy(prompts[i].reply->data, resp[pam_prompts].resp, len);
-	prompts[i].reply->length = len;
-    }
-
-cleanup:
-    /* pam_prompts is correct at this point */
-
-    for (i = 0; i < pam_prompts; i++) {
-	if (msg[i].msg)
-	    free(msg[i].msg);
-    }
-    free(msg);
-
-    if (resp) {
-	for (i = 0; i < pam_prompts; i++) {
-	    /*
-	     * Note that PAM is underspecified wrt free()'ing resp[i].resp.
-	     * It's not clear if I should free it, or if the application
-	     * has to. Therefore most (all?) apps won't free() it, and I
-	     * can't either, as I am not sure it was malloc()'d. All PAM
-	     * implementations I've seen leak memory here. Not so bad, IFF
-	     * you fork/exec for each PAM authentication (as is typical).
-	     */
-#if 0
-	    if (resp[i].resp)
-		free(resp[i].resp);
-#endif /* 0 */
-	}
-	/* This does not lose resp[i].resp if the application saved a copy. */
-	free(resp);
-    }
-
-    return (pamret ? KRB5KRB_ERR_GENERIC : 0);
-}
-
-
 /*
  * This routine with some modification is from the MIT V5B6 appl/bsd/login.c
+ * Modified by Sam Hartman <hartmans@mit.edu> to support PAM services
+ * for Debian.
  *
  * Verify the Kerberos ticket-granting ticket just retrieved for the
  * user.  If the Kerberos server doesn't respond, assume the user is
  * trying to fake us out (since we DID just get a TGT from what is
  * supposedly our KDC).  If the host/<host> service is unknown (i.e.,
- * the local keytab doesn't have it), let her in.
+ * the local keytab doesn't have it), and we cannot find another
+ * service we do have, let her in.
  *
  * Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
  */
 int
-verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache, int debug)
+verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache,
+		  char * pam_service, int debug)
 {
     char		phost[BUFSIZ];
-    krb5_error_code	retval;
+    char *services [3];
+    char **service;
+    krb5_error_code	retval = -1;
     krb5_principal	princ;
     krb5_keyblock *	keyblock = 0;
     krb5_data		packet;
     krb5_auth_context	auth_context = NULL;
-    krb5_keytab		keytab = NULL;
-    char *		kt_name = NULL;
 
     packet.data = 0;
 
     /*
-     * Get the server principal for the local host.
-     * (Use defaults of "host" and canonicalized local name.)
-     */
-    if (retval = krb5_sname_to_principal(context, NULL, NULL,
-					 KRB5_NT_SRV_HST, &princ)) {
+    * If possible we want to try and verify the ticket we have
+    * received against a keytab.  We will try multiple service
+    * principals, including at least the host principal and the PAM
+    * service principal.  The host principal is preferred because access
+    * to that key is generally sufficient to compromise root, while the
+    *     service key for this PAM service may be less carefully guarded.
+    * It is important to check the keytab first before the KDC so we do
+    * not get spoofed by a fake  KDC.*/
+    services [0] = "host";
+    services [1] = pam_service;
+    services [2] = NULL;
+    for ( service = &services[0]; *service != NULL; service++ ) {
+      if ((retval = krb5_sname_to_principal(context, NULL, *service, KRB5_NT_SRV_HST,
+					    &princ)) != 0) {
 	if (debug)
-	    syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
-		   "krb5_sname_to_principal()", error_message(retval));
+	  syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
+		 "krb5_sname_to_principal()", error_message(retval));
 	return -1;
-    }
+      }
 
-    /* Extract the name directly. */
-    strncpy(phost, krb5_princ_component(c, princ, 1)->data, BUFSIZ);
-    phost[BUFSIZ - 1] = '\0';
-
-    /*
-     * Do we have host/<host> keys?
-     * (use default/configured keytab, kvno IGNORE_VNO to get the
-     * first match, and enctype is currently ignored anyhow.)
-     */
-    if (retval = krb5_kt_read_service_key(context, NULL, princ, 0,
-					  ENCTYPE_DES_CBC_MD5, &keyblock)) {
+      /* Extract the name directly. */
+      strncpy(phost, compat_princ_component(context, princ, 1), BUFSIZ);
+      phost[BUFSIZ - 1] = '\0';
+
+      /*
+       * Do we have service/<host> keys?
+       * (use default/configured keytab, kvno IGNORE_VNO to get the
+       * first match, and ignore enctype.)
+       */
+      if ((retval = krb5_kt_read_service_key(context, NULL, princ, 0,
+					     0, &keyblock)) != 0)
+	continue;
+      break;
+    }
+    if (retval != 0 ) {		/* failed to find key */
 	/* Keytab or service key does not exist */
 	if (debug)
 	    syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
@@ -228,7 +132,7 @@
 	krb5_free_keyblock(context, keyblock);
 
     /* Talk to the kdc and construct the ticket. */
-    retval = krb5_mk_req(context, &auth_context, 0, "host", phost,
+    retval = krb5_mk_req(context, &auth_context, 0, *service, phost,
 			 NULL, ccache, &packet);
     if (auth_context) {
 	krb5_auth_con_free(context, auth_context);
@@ -256,7 +160,7 @@
 
 cleanup:
     if (packet.data)
-	krb5_free_data_contents(context, &packet);
+	compat_free_data_contents(context, &packet);
     krb5_free_principal(context, princ);
     return retval;
 
