d0ca5af855957b0aaa43ed0a11e151faf64c68c2
[moonshot.git] / moonshot / mech_eap / util_cred.c
1 /*
2  * Copyright (c) 2011, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 /*
34  * Utility routines for credential handles.
35  */
36
37 #include "gssapiP_eap.h"
38
39 #include <pwd.h>
40 #include <stdio.h> /* for BUFSIZ */
41
42 OM_uint32
43 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred)
44 {
45     OM_uint32 tmpMinor;
46     gss_cred_id_t cred;
47
48     *pCred = GSS_C_NO_CREDENTIAL;
49
50     cred = (gss_cred_id_t)GSSEAP_CALLOC(1, sizeof(*cred));
51     if (cred == NULL) {
52         *minor = ENOMEM;
53         return GSS_S_FAILURE;
54     }
55
56     if (GSSEAP_MUTEX_INIT(&cred->mutex) != 0) {
57         *minor = errno;
58         gssEapReleaseCred(&tmpMinor, &cred);
59         return GSS_S_FAILURE;
60     }
61
62     *pCred = cred;
63
64     *minor = 0;
65     return GSS_S_COMPLETE;
66 }
67
68 OM_uint32
69 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
70 {
71     OM_uint32 tmpMinor;
72     gss_cred_id_t cred = *pCred;
73     krb5_context krbContext = NULL;
74
75     if (cred == GSS_C_NO_CREDENTIAL) {
76         return GSS_S_COMPLETE;
77     }
78
79     GSSEAP_KRB_INIT(&krbContext);
80
81     gssEapReleaseName(&tmpMinor, &cred->name);
82
83     if (cred->password.value != NULL) {
84         memset(cred->password.value, 0, cred->password.length);
85         GSSEAP_FREE(cred->password.value);
86     }
87
88     if (cred->radiusConfigFile != NULL)
89         GSSEAP_FREE(cred->radiusConfigFile);
90     if (cred->radiusConfigStanza != NULL)
91         GSSEAP_FREE(cred->radiusConfigStanza);
92
93 #ifdef GSSEAP_ENABLE_REAUTH
94     if (cred->krbCredCache != NULL) {
95         if (cred->flags & CRED_FLAG_DEFAULT_CCACHE)
96             krb5_cc_close(krbContext, cred->krbCredCache);
97         else
98             krb5_cc_destroy(krbContext, cred->krbCredCache);
99     }
100     if (cred->reauthCred != GSS_C_NO_CREDENTIAL)
101         gssReleaseCred(&tmpMinor, &cred->reauthCred);
102 #endif
103
104     GSSEAP_MUTEX_DESTROY(&cred->mutex);
105     memset(cred, 0, sizeof(*cred));
106     GSSEAP_FREE(cred);
107     *pCred = NULL;
108
109     *minor = 0;
110     return GSS_S_COMPLETE;
111 }
112
113 static OM_uint32
114 readDefaultIdentityAndCreds(OM_uint32 *minor,
115                             gss_buffer_t defaultIdentity,
116                             gss_buffer_t defaultCreds)
117 {
118     OM_uint32 major, tmpMinor;
119     FILE *fp = NULL;
120     char pwbuf[BUFSIZ], buf[BUFSIZ];
121     char *ccacheName;
122     struct passwd *pw = NULL, pwd;
123
124     defaultIdentity->length = 0;
125     defaultIdentity->value = NULL;
126
127     defaultCreds->length = 0;
128     defaultCreds->value = NULL;
129
130     ccacheName = getenv("GSSEAP_IDENTITY");
131     if (ccacheName == NULL) {
132         if (getpwuid_r(getuid(), &pwd, pwbuf, sizeof(pwbuf), &pw) != 0 ||
133             pw == NULL || pw->pw_dir == NULL) {
134             major = GSS_S_CRED_UNAVAIL;
135             *minor = errno;
136             goto cleanup;
137         }
138
139         snprintf(buf, sizeof(buf), "%s/.gss_eap_id", pw->pw_dir);
140         ccacheName = buf;
141     }
142
143     fp = fopen(ccacheName, "r");
144     if (fp == NULL) {
145         major = GSS_S_CRED_UNAVAIL;
146         *minor = GSSEAP_NO_DEFAULT_CRED;
147         goto cleanup;
148     }
149
150     while (fgets(buf, sizeof(buf), fp) != NULL) {
151         gss_buffer_desc src, *dst;
152
153         src.length = strlen(buf);
154         src.value = buf;
155
156         if (src.length == 0)
157             break;
158
159         if (buf[src.length - 1] == '\n') {
160             buf[src.length - 1] = '\0';
161             if (--src.length == 0)
162                 break;
163         }
164
165         if (defaultIdentity->value == NULL)
166             dst = defaultIdentity;
167         else if (defaultCreds->value == NULL)
168             dst = defaultCreds;
169         else
170             break;
171
172         major = duplicateBuffer(minor, &src, dst);
173         if (GSS_ERROR(major))
174             goto cleanup;
175     }
176
177     if (defaultIdentity->length == 0) {
178         major = GSS_S_CRED_UNAVAIL;
179         *minor = GSSEAP_NO_DEFAULT_CRED;
180         goto cleanup;
181     }
182
183     major = GSS_S_COMPLETE;
184     *minor = 0;
185
186 cleanup:
187     if (fp != NULL)
188         fclose(fp);
189
190     if (GSS_ERROR(major)) {
191         gss_release_buffer(&tmpMinor, defaultIdentity);
192         gss_release_buffer(&tmpMinor, defaultCreds);
193     }
194
195     return major;
196 }
197
198 OM_uint32
199 gssEapAcquireCred(OM_uint32 *minor,
200                   const gss_name_t desiredName,
201                   const gss_buffer_t password,
202                   OM_uint32 timeReq GSSEAP_UNUSED,
203                   const gss_OID_set desiredMechs,
204                   int credUsage,
205                   gss_cred_id_t *pCred,
206                   gss_OID_set *pActualMechs,
207                   OM_uint32 *timeRec)
208 {
209     OM_uint32 major, tmpMinor;
210     gss_cred_id_t cred;
211     gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
212     gss_name_t defaultIdentityName = GSS_C_NO_NAME;
213     gss_buffer_desc defaultCreds = GSS_C_EMPTY_BUFFER;
214     gss_OID nameMech = GSS_C_NO_OID;
215
216     /* XXX TODO validate with changed set_cred_option API */
217     *pCred = GSS_C_NO_CREDENTIAL;
218
219     major = gssEapAllocCred(minor, &cred);
220     if (GSS_ERROR(major))
221         goto cleanup;
222
223     switch (credUsage) {
224     case GSS_C_BOTH:
225         cred->flags |= CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT;
226         break;
227     case GSS_C_INITIATE:
228         cred->flags |= CRED_FLAG_INITIATE;
229         break;
230     case GSS_C_ACCEPT:
231         cred->flags |= CRED_FLAG_ACCEPT;
232         break;
233     default:
234         major = GSS_S_FAILURE;
235         *minor = GSSEAP_BAD_USAGE;
236         goto cleanup;
237         break;
238     }
239
240     major = gssEapValidateMechs(minor, desiredMechs);
241     if (GSS_ERROR(major))
242         goto cleanup;
243
244     major = duplicateOidSet(minor, desiredMechs, &cred->mechanisms);
245     if (GSS_ERROR(major))
246         goto cleanup;
247
248     if (cred->mechanisms != GSS_C_NO_OID_SET &&
249         cred->mechanisms->count == 1)
250         nameMech = &cred->mechanisms->elements[0];
251
252     if (cred->flags & CRED_FLAG_INITIATE) {
253         major = readDefaultIdentityAndCreds(minor, &defaultIdentity, &defaultCreds);
254         if (major == GSS_S_COMPLETE) {
255             major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
256                                      nameMech, &defaultIdentityName);
257             if (GSS_ERROR(major))
258                 goto cleanup;
259         } else if (major != GSS_S_CRED_UNAVAIL)
260             goto cleanup;
261     }
262
263     if (desiredName != GSS_C_NO_NAME) {
264         GSSEAP_MUTEX_LOCK(&desiredName->mutex);
265
266         major = gssEapDuplicateName(minor, desiredName, &cred->name);
267         if (GSS_ERROR(major)) {
268             GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
269             goto cleanup;
270         }
271
272         GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
273
274         if (defaultIdentityName != GSS_C_NO_NAME) {
275             int nameEqual;
276
277             major = gssEapCompareName(minor, desiredName,
278                                       defaultIdentityName, &nameEqual);
279             if (GSS_ERROR(major))
280                 goto cleanup;
281             else if (nameEqual)
282                 cred->flags |= CRED_FLAG_DEFAULT_IDENTITY;
283         }
284     } else {
285         if (cred->flags & CRED_FLAG_ACCEPT) {
286             gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
287             char serviceName[5 + MAXHOSTNAMELEN];
288
289             /* default host-based service is host@localhost */
290             memcpy(serviceName, "host@", 5);
291             if (gethostname(&serviceName[5], MAXHOSTNAMELEN) != 0) {
292                 major = GSS_S_FAILURE;
293                 *minor = GSSEAP_NO_HOSTNAME;
294                 goto cleanup;
295             }
296
297             nameBuf.value = serviceName;
298             nameBuf.length = strlen((char *)nameBuf.value);
299
300             major = gssEapImportName(minor, &nameBuf, GSS_C_NT_HOSTBASED_SERVICE,
301                                      nameMech, &cred->name);
302             if (GSS_ERROR(major))
303                 goto cleanup;
304         } else if (cred->flags & CRED_FLAG_INITIATE) {
305             if (defaultIdentityName == GSS_C_NO_NAME) {
306                 major = GSS_S_CRED_UNAVAIL;
307                 *minor = GSSEAP_NO_DEFAULT_IDENTITY;
308                 goto cleanup;
309             }
310
311             cred->name = defaultIdentityName;
312             defaultIdentityName = GSS_C_NO_NAME;
313         }
314         cred->flags |= CRED_FLAG_DEFAULT_IDENTITY;
315     }
316
317     assert(cred->name != GSS_C_NO_NAME);
318
319     if (password != GSS_C_NO_BUFFER) {
320         major = duplicateBuffer(minor, password, &cred->password);
321         if (GSS_ERROR(major))
322             goto cleanup;
323
324         cred->flags |= CRED_FLAG_PASSWORD;
325     } else if (defaultCreds.value != NULL &&
326         (cred->flags & CRED_FLAG_DEFAULT_IDENTITY)) {
327         cred->password = defaultCreds;
328
329         defaultCreds.length = 0;
330         defaultCreds.value = NULL;
331
332         cred->flags |= CRED_FLAG_PASSWORD;
333     } else if (cred->flags & CRED_FLAG_INITIATE) {
334         /*
335          * OK, here we need to ask the supplicant if we have creds or it
336          * will acquire them, so GS2 can know whether to prompt for a
337          * password or not.
338          */
339 #if 0
340         && !gssEapCanReauthP(cred, GSS_C_NO_NAME, timeReq)
341 #endif
342         major = GSS_S_CRED_UNAVAIL;
343         *minor = GSSEAP_NO_DEFAULT_CRED;
344         goto cleanup;
345     }
346
347     if (pActualMechs != NULL) {
348         major = duplicateOidSet(minor, cred->mechanisms, pActualMechs);
349         if (GSS_ERROR(major))
350             goto cleanup;
351     }
352
353     if (timeRec != NULL)
354         *timeRec = GSS_C_INDEFINITE;
355
356     *pCred = cred;
357
358     major = GSS_S_COMPLETE;
359     *minor = 0;
360
361 cleanup:
362     if (GSS_ERROR(major))
363         gssEapReleaseCred(&tmpMinor, &cred);
364     gssEapReleaseName(&tmpMinor, &defaultIdentityName);
365     gss_release_buffer(&tmpMinor, &defaultIdentity);
366     if (defaultCreds.value != NULL) {
367         memset(defaultCreds.value, 0, defaultCreds.length);
368         gss_release_buffer(&tmpMinor, &defaultCreds);
369     }
370
371     return major;
372 }
373
374 /*
375  * Return TRUE if cred available for mechanism. Caller need no acquire
376  * lock because mechanisms list is immutable.
377  */
378 int
379 gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech)
380 {
381     OM_uint32 minor;
382     int present = 0;
383
384     assert(mech != GSS_C_NO_OID);
385
386     if (cred == GSS_C_NO_CREDENTIAL || cred->mechanisms == GSS_C_NO_OID_SET)
387         return TRUE;
388
389     gss_test_oid_set_member(&minor, mech, cred->mechanisms, &present);
390
391     return present;
392 }
393
394 OM_uint32
395 gssEapInquireCred(OM_uint32 *minor,
396                   gss_cred_id_t cred,
397                   gss_name_t *name,
398                   OM_uint32 *pLifetime,
399                   gss_cred_usage_t *cred_usage,
400                   gss_OID_set *mechanisms)
401 {
402     OM_uint32 major;
403     time_t now, lifetime;
404
405     if (name != NULL) {
406         major = gssEapDuplicateName(minor, cred->name, name);
407         if (GSS_ERROR(major))
408             return major;
409     }
410
411     if (cred_usage != NULL) {
412         OM_uint32 flags = (cred->flags & (CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT));
413
414         switch (flags) {
415         case CRED_FLAG_INITIATE:
416             *cred_usage = GSS_C_INITIATE;
417             break;
418         case CRED_FLAG_ACCEPT:
419             *cred_usage = GSS_C_ACCEPT;
420             break;
421         default:
422             *cred_usage = GSS_C_BOTH;
423             break;
424         }
425     }
426
427     if (mechanisms != NULL) {
428         if (cred->mechanisms != GSS_C_NO_OID_SET)
429             major = duplicateOidSet(minor, cred->mechanisms, mechanisms);
430         else
431             major = gssEapIndicateMechs(minor, mechanisms);
432         if (GSS_ERROR(major))
433             return major;
434     }
435
436     if (cred->expiryTime == 0) {
437         lifetime = GSS_C_INDEFINITE;
438     } else  {
439         now = time(NULL);
440         lifetime = now - cred->expiryTime;
441         if (lifetime < 0)
442             lifetime = 0;
443     }
444
445     if (pLifetime != NULL) {
446         *pLifetime = lifetime;
447     }
448
449     if (lifetime == 0) {
450         *minor = GSSEAP_CRED_EXPIRED;
451         return GSS_S_CREDENTIALS_EXPIRED;
452     }
453
454     major = GSS_S_COMPLETE;
455     *minor = 0;
456
457     return major;
458 }