Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / lib / libpam / modules / pam_krb5 / support.c
1 /*
2  * support.c
3  *
4  * Support functions for pam_krb5
5  *
6  * $FreeBSD: src/lib/libpam/modules/pam_krb5/support.c,v 1.1.2.1 2001/06/07 09:37:07 markm Exp $
7  * $DragonFly: src/lib/libpam/modules/pam_krb5/Attic/support.c,v 1.2 2003/06/17 04:26:50 dillon Exp $
8  */
9
10 static const char rcsid[] = "$Id: support.c,v 1.8 2000/01/04 09:50:03 fcusack Exp $";
11
12 #include <errno.h>
13 #include <stdio.h>      /* BUFSIZ */
14 #include <stdlib.h>     /* malloc */
15 #include <string.h>     /* strncpy */
16 #include <syslog.h>     /* syslog */
17 #include <security/pam_appl.h>
18 #include <security/pam_modules.h>
19 #include <krb5.h>
20 #include <com_err.h>
21 #include "pam_krb5.h"
22
23 /*
24  * Get info from the user. Disallow null responses (regardless of flags).
25  * response gets allocated and filled in on successful return. Caller
26  * is responsible for freeing it.
27  */
28 int
29 get_user_info(pam_handle_t *pamh, char *prompt, int type, char **response)
30 {
31     int pamret;
32     struct pam_message  msg;
33     const struct pam_message *pmsg;
34     struct pam_response *resp = NULL;
35     struct pam_conv     *conv;
36
37     if ((pamret = pam_get_item(pamh, PAM_CONV, (const void **) &conv)) != 0)
38         return pamret;
39
40     /* set up conversation call */
41     pmsg = &msg;
42     msg.msg_style = type;
43     msg.msg = prompt;
44
45     if ((pamret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr)) != 0)
46         return pamret;
47
48     /* Caller should ignore errors for non-response conversations */
49     if (!resp)
50         return PAM_CONV_ERR;
51
52     if (!(resp->resp && resp->resp[0])) {
53         free(resp);
54         return PAM_AUTH_ERR;
55     }
56
57     *response = resp->resp;
58     free(resp);
59     return pamret;
60 }
61
62 /*
63  * This routine with some modification is from the MIT V5B6 appl/bsd/login.c
64  * Modified by Sam Hartman <hartmans@mit.edu> to support PAM services
65  * for Debian.
66  *
67  * Verify the Kerberos ticket-granting ticket just retrieved for the
68  * user.  If the Kerberos server doesn't respond, assume the user is
69  * trying to fake us out (since we DID just get a TGT from what is
70  * supposedly our KDC).  If the host/<host> service is unknown (i.e.,
71  * the local keytab doesn't have it), and we cannot find another
72  * service we do have, let her in.
73  *
74  * Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
75  */
76 int
77 verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache,
78                   char * pam_service, int debug)
79 {
80     char                phost[BUFSIZ];
81     char *services [3];
82     char **service;
83     krb5_error_code     retval = -1;
84     krb5_principal      princ;
85     krb5_keyblock *     keyblock = 0;
86     krb5_data           packet;
87     krb5_auth_context   auth_context = NULL;
88
89     packet.data = 0;
90
91     /*
92     * If possible we want to try and verify the ticket we have
93     * received against a keytab.  We will try multiple service
94     * principals, including at least the host principal and the PAM
95     * service principal.  The host principal is preferred because access
96     * to that key is generally sufficient to compromise root, while the
97     *     service key for this PAM service may be less carefully guarded.
98     * It is important to check the keytab first before the KDC so we do
99     * not get spoofed by a fake  KDC.*/
100     services [0] = "host";
101     services [1] = pam_service;
102     services [2] = NULL;
103     for ( service = &services[0]; *service != NULL; service++ ) {
104       if ((retval = krb5_sname_to_principal(context, NULL, *service, KRB5_NT_SRV_HST,
105                                             &princ)) != 0) {
106         if (debug)
107           syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
108                  "krb5_sname_to_principal()", error_message(retval));
109         return -1;
110       }
111
112       /* Extract the name directly. */
113       strncpy(phost, compat_princ_component(context, princ, 1), BUFSIZ);
114       phost[BUFSIZ - 1] = '\0';
115
116       /*
117        * Do we have service/<host> keys?
118        * (use default/configured keytab, kvno IGNORE_VNO to get the
119        * first match, and ignore enctype.)
120        */
121       if ((retval = krb5_kt_read_service_key(context, NULL, princ, 0,
122                                              0, &keyblock)) != 0)
123         continue;
124       break;
125     }
126     if (retval != 0 ) {         /* failed to find key */
127         /* Keytab or service key does not exist */
128         if (debug)
129             syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
130                    "krb5_kt_read_service_key()", error_message(retval));
131         retval = 0;
132         goto cleanup;
133     }
134     if (keyblock)
135         krb5_free_keyblock(context, keyblock);
136
137     /* Talk to the kdc and construct the ticket. */
138     retval = krb5_mk_req(context, &auth_context, 0, *service, phost,
139                          NULL, ccache, &packet);
140     if (auth_context) {
141         krb5_auth_con_free(context, auth_context);
142         auth_context = NULL; /* setup for rd_req */
143     }
144     if (retval) {
145         if (debug)
146             syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
147                    "krb5_mk_req()", error_message(retval));
148         retval = -1;
149         goto cleanup;
150     }
151
152     /* Try to use the ticket. */
153     retval = krb5_rd_req(context, &auth_context, &packet, princ,
154                          NULL, NULL, NULL);
155     if (retval) {
156         if (debug)
157             syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
158                    "krb5_rd_req()", error_message(retval));
159         retval = -1;
160     } else {
161         retval = 1;
162     }
163
164 cleanup:
165     if (packet.data)
166         compat_free_data_contents(context, &packet);
167     krb5_free_principal(context, princ);
168     return retval;
169
170 }
171
172
173 /* Free the memory for cache_name. Called by pam_end() */
174 void
175 cleanup_cache(pam_handle_t *pamh, void *data, int pam_end_status)
176 {
177     krb5_context        pam_context;
178     krb5_ccache         ccache;
179
180     if (krb5_init_context(&pam_context))
181         return;
182
183     ccache = (krb5_ccache) data;
184     (void) krb5_cc_destroy(pam_context, ccache);
185     krb5_free_context(pam_context);
186 }