b481118b4750135eb6f29d9612575540c95c4388
[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
41 const gss_OID_desc gssEapPasswordCredType =
42     { 7, "\x2a\x85\x70\x2b\x0d\x81\x48" };
43
44 OM_uint32
45 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred)
46 {
47     OM_uint32 tmpMinor;
48     gss_cred_id_t cred;
49
50     *pCred = GSS_C_NO_CREDENTIAL;
51
52     cred = (gss_cred_id_t)GSSEAP_CALLOC(1, sizeof(*cred));
53     if (cred == NULL) {
54         *minor = ENOMEM;
55         return GSS_S_FAILURE;
56     }
57
58     if (GSSEAP_MUTEX_INIT(&cred->mutex) != 0) {
59         *minor = errno;
60         gssEapReleaseCred(&tmpMinor, &cred);
61         return GSS_S_FAILURE;
62     }
63
64     *pCred = cred;
65
66     *minor = 0;
67     return GSS_S_COMPLETE;
68 }
69
70 OM_uint32
71 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
72 {
73     OM_uint32 tmpMinor;
74     gss_cred_id_t cred = *pCred;
75     krb5_context krbContext = NULL;
76
77     if (cred == GSS_C_NO_CREDENTIAL) {
78         return GSS_S_COMPLETE;
79     }
80
81     GSSEAP_KRB_INIT(&krbContext);
82
83     gssEapReleaseName(&tmpMinor, &cred->name);
84
85     if (cred->password.value != NULL) {
86         memset(cred->password.value, 0, cred->password.length);
87         GSSEAP_FREE(cred->password.value);
88     }
89
90     if (cred->radiusConfigFile != NULL)
91         GSSEAP_FREE(cred->radiusConfigFile);
92     if (cred->radiusConfigStanza != NULL)
93         GSSEAP_FREE(cred->radiusConfigStanza);
94
95 #ifdef GSSEAP_ENABLE_REAUTH
96     if (cred->krbCredCache != NULL) {
97         if (cred->flags & CRED_FLAG_DEFAULT_CCACHE)
98             krb5_cc_close(krbContext, cred->krbCredCache);
99         else
100             krb5_cc_destroy(krbContext, cred->krbCredCache);
101     }
102     if (cred->reauthCred != GSS_C_NO_CREDENTIAL)
103         gssReleaseCred(&tmpMinor, &cred->reauthCred);
104 #endif
105
106     GSSEAP_MUTEX_DESTROY(&cred->mutex);
107     memset(cred, 0, sizeof(*cred));
108     GSSEAP_FREE(cred);
109     *pCred = NULL;
110
111     *minor = 0;
112     return GSS_S_COMPLETE;
113 }
114
115 static OM_uint32
116 readDefaultIdentityAndCreds(OM_uint32 *minor,
117                             gss_buffer_t defaultIdentity,
118                             gss_buffer_t defaultCreds)
119 {
120     OM_uint32 major, tmpMinor;
121     FILE *fp = NULL;
122     char pwbuf[BUFSIZ], buf[BUFSIZ];
123     char *ccacheName;
124     struct passwd *pw = NULL, pwd;
125
126     defaultIdentity->length = 0;
127     defaultIdentity->value = NULL;
128
129     defaultCreds->length = 0;
130     defaultCreds->value = NULL;
131
132     ccacheName = getenv("GSSEAP_IDENTITY");
133     if (ccacheName == NULL) {
134         if (getpwuid_r(getuid(), &pwd, pwbuf, sizeof(pwbuf), &pw) != 0 ||
135             pw == NULL || pw->pw_dir == NULL) {
136             major = GSS_S_CRED_UNAVAIL;
137             *minor = errno;
138             goto cleanup;
139         }
140
141         snprintf(buf, sizeof(buf), "%s/.gss_eap_id", pw->pw_dir);
142         ccacheName = buf;
143     }
144
145     fp = fopen(ccacheName, "r");
146     if (fp == NULL) {
147         major = GSS_S_CRED_UNAVAIL;
148         *minor = GSSEAP_NO_DEFAULT_CRED;
149         goto cleanup;
150     }
151
152     while (fgets(buf, sizeof(buf), fp) != NULL) {
153         gss_buffer_desc src, *dst;
154
155         src.length = strlen(buf);
156         src.value = buf;
157
158         if (src.length == 0)
159             break;
160
161         if (buf[src.length - 1] == '\n') {
162             buf[src.length - 1] = '\0';
163             if (--src.length == 0)
164                 break;
165         }
166
167         if (defaultIdentity->value == NULL)
168             dst = defaultIdentity;
169         else if (defaultCreds->value == NULL)
170             dst = defaultCreds;
171         else
172             break;
173
174         major = duplicateBuffer(minor, &src, dst);
175         if (GSS_ERROR(major))
176             goto cleanup;
177     }
178
179     if (defaultIdentity->length == 0) {
180         major = GSS_S_CRED_UNAVAIL;
181         *minor = GSSEAP_NO_DEFAULT_CRED;
182         goto cleanup;
183     }
184
185     major = GSS_S_COMPLETE;
186     *minor = 0;
187
188 cleanup:
189     if (fp != NULL)
190         fclose(fp);
191
192     if (GSS_ERROR(major)) {
193         gss_release_buffer(&tmpMinor, defaultIdentity);
194         gss_release_buffer(&tmpMinor, defaultCreds);
195     }
196
197     return major;
198 }
199
200 OM_uint32
201 gssEapAcquireCred(OM_uint32 *minor,
202                   const gss_name_t desiredName,
203                   gss_const_OID credType,
204                   const void *credData,
205                   OM_uint32 timeReq GSSEAP_UNUSED,
206                   const gss_OID_set desiredMechs,
207                   int credUsage,
208                   gss_cred_id_t *pCred,
209                   gss_OID_set *pActualMechs,
210                   OM_uint32 *timeRec)
211 {
212     OM_uint32 major, tmpMinor;
213     gss_cred_id_t cred;
214     gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
215     gss_name_t defaultIdentityName = GSS_C_NO_NAME;
216     gss_buffer_desc defaultCreds = GSS_C_EMPTY_BUFFER;
217     gss_OID nameMech = GSS_C_NO_OID;
218     gss_buffer_t password = GSS_C_NO_BUFFER;
219
220     /* XXX TODO validate with changed set_cred_option API */
221     *pCred = GSS_C_NO_CREDENTIAL;
222
223     if (credType != GSS_C_NO_OID) {
224         if (oidEqual(credType, &gssEapPasswordCredType)) {
225             password = (gss_buffer_t)credData;
226         } else {
227             major = GSS_S_CRED_UNAVAIL;
228             *minor = GSSEAP_BAD_CRED_TYPE;
229             goto cleanup;
230         }
231     }
232
233     major = gssEapAllocCred(minor, &cred);
234     if (GSS_ERROR(major))
235         goto cleanup;
236
237     switch (credUsage) {
238     case GSS_C_BOTH:
239         cred->flags |= CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT;
240         break;
241     case GSS_C_INITIATE:
242         cred->flags |= CRED_FLAG_INITIATE;
243         break;
244     case GSS_C_ACCEPT:
245         cred->flags |= CRED_FLAG_ACCEPT;
246         break;
247     default:
248         major = GSS_S_FAILURE;
249         *minor = GSSEAP_BAD_USAGE;
250         goto cleanup;
251         break;
252     }
253
254     major = gssEapValidateMechs(minor, desiredMechs);
255     if (GSS_ERROR(major))
256         goto cleanup;
257
258     major = duplicateOidSet(minor, desiredMechs, &cred->mechanisms);
259     if (GSS_ERROR(major))
260         goto cleanup;
261
262     if (cred->mechanisms != GSS_C_NO_OID_SET &&
263         cred->mechanisms->count == 1)
264         nameMech = &cred->mechanisms->elements[0];
265
266     if (cred->flags & CRED_FLAG_INITIATE) {
267         major = readDefaultIdentityAndCreds(minor, &defaultIdentity, &defaultCreds);
268         if (major == GSS_S_COMPLETE) {
269             major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
270                                      nameMech, &defaultIdentityName);
271             if (GSS_ERROR(major))
272                 goto cleanup;
273         } else if (major != GSS_S_CRED_UNAVAIL)
274             goto cleanup;
275     }
276
277     if (desiredName != GSS_C_NO_NAME) {
278         GSSEAP_MUTEX_LOCK(&desiredName->mutex);
279
280         major = gssEapDuplicateName(minor, desiredName, &cred->name);
281         if (GSS_ERROR(major)) {
282             GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
283             goto cleanup;
284         }
285
286         GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
287
288         if (defaultIdentityName != GSS_C_NO_NAME) {
289             int nameEqual;
290
291             major = gssEapCompareName(minor, desiredName,
292                                       defaultIdentityName, &nameEqual);
293             if (GSS_ERROR(major))
294                 goto cleanup;
295             else if (nameEqual)
296                 cred->flags |= CRED_FLAG_DEFAULT_IDENTITY;
297         }
298     } else {
299         if (cred->flags & CRED_FLAG_ACCEPT) {
300             gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
301             char serviceName[5 + MAXHOSTNAMELEN];
302
303             /* default host-based service is host@localhost */
304             memcpy(serviceName, "host@", 5);
305             if (gethostname(&serviceName[5], MAXHOSTNAMELEN) != 0) {
306                 major = GSS_S_FAILURE;
307                 *minor = GSSEAP_NO_HOSTNAME;
308                 goto cleanup;
309             }
310
311             nameBuf.value = serviceName;
312             nameBuf.length = strlen((char *)nameBuf.value);
313
314             major = gssEapImportName(minor, &nameBuf, GSS_C_NT_HOSTBASED_SERVICE,
315                                      nameMech, &cred->name);
316             if (GSS_ERROR(major))
317                 goto cleanup;
318         } else if (cred->flags & CRED_FLAG_INITIATE) {
319             if (defaultIdentityName == GSS_C_NO_NAME) {
320                 major = GSS_S_CRED_UNAVAIL;
321                 *minor = GSSEAP_NO_DEFAULT_IDENTITY;
322                 goto cleanup;
323             }
324
325             cred->name = defaultIdentityName;
326             defaultIdentityName = GSS_C_NO_NAME;
327         }
328         cred->flags |= CRED_FLAG_DEFAULT_IDENTITY;
329     }
330
331     assert(cred->name != GSS_C_NO_NAME);
332
333     if (password != GSS_C_NO_BUFFER) {
334         major = duplicateBuffer(minor, password, &cred->password);
335         if (GSS_ERROR(major))
336             goto cleanup;
337
338         cred->flags |= CRED_FLAG_PASSWORD;
339     } else if (defaultCreds.value != NULL &&
340         (cred->flags & CRED_FLAG_DEFAULT_IDENTITY)) {
341         cred->password = defaultCreds;
342
343         defaultCreds.length = 0;
344         defaultCreds.value = NULL;
345
346         cred->flags |= CRED_FLAG_PASSWORD;
347     } else if (cred->flags & CRED_FLAG_INITIATE) {
348         /*
349          * OK, here we need to ask the supplicant if we have creds or it
350          * will acquire them, so GS2 can know whether to prompt for a
351          * password or not.
352          */
353 #if 0
354         && !gssEapCanReauthP(cred, GSS_C_NO_NAME, timeReq)
355 #endif
356         major = GSS_S_CRED_UNAVAIL;
357         *minor = GSSEAP_NO_DEFAULT_CRED;
358         goto cleanup;
359     }
360
361     if (pActualMechs != NULL) {
362         major = duplicateOidSet(minor, cred->mechanisms, pActualMechs);
363         if (GSS_ERROR(major))
364             goto cleanup;
365     }
366
367     if (timeRec != NULL)
368         *timeRec = GSS_C_INDEFINITE;
369
370     *pCred = cred;
371
372     major = GSS_S_COMPLETE;
373     *minor = 0;
374
375 cleanup:
376     if (GSS_ERROR(major))
377         gssEapReleaseCred(&tmpMinor, &cred);
378     gssEapReleaseName(&tmpMinor, &defaultIdentityName);
379     gss_release_buffer(&tmpMinor, &defaultIdentity);
380     if (defaultCreds.value != NULL) {
381         memset(defaultCreds.value, 0, defaultCreds.length);
382         gss_release_buffer(&tmpMinor, &defaultCreds);
383     }
384
385     return major;
386 }
387
388 /*
389  * Return TRUE if cred available for mechanism. Caller need no acquire
390  * lock because mechanisms list is immutable.
391  */
392 int
393 gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech)
394 {
395     OM_uint32 minor;
396     int present = 0;
397
398     assert(mech != GSS_C_NO_OID);
399
400     if (cred == GSS_C_NO_CREDENTIAL || cred->mechanisms == GSS_C_NO_OID_SET)
401         return TRUE;
402
403     gss_test_oid_set_member(&minor, mech, cred->mechanisms, &present);
404
405     return present;
406 }
407
408 OM_uint32
409 gssEapInquireCred(OM_uint32 *minor,
410                   gss_cred_id_t cred,
411                   gss_name_t *name,
412                   OM_uint32 *pLifetime,
413                   gss_cred_usage_t *cred_usage,
414                   gss_OID_set *mechanisms)
415 {
416     OM_uint32 major;
417     time_t now, lifetime;
418
419     if (name != NULL) {
420         major = gssEapDuplicateName(minor, cred->name, name);
421         if (GSS_ERROR(major))
422             return major;
423     }
424
425     if (cred_usage != NULL) {
426         OM_uint32 flags = (cred->flags & (CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT));
427
428         switch (flags) {
429         case CRED_FLAG_INITIATE:
430             *cred_usage = GSS_C_INITIATE;
431             break;
432         case CRED_FLAG_ACCEPT:
433             *cred_usage = GSS_C_ACCEPT;
434             break;
435         default:
436             *cred_usage = GSS_C_BOTH;
437             break;
438         }
439     }
440
441     if (mechanisms != NULL) {
442         if (cred->mechanisms != GSS_C_NO_OID_SET)
443             major = duplicateOidSet(minor, cred->mechanisms, mechanisms);
444         else
445             major = gssEapIndicateMechs(minor, mechanisms);
446         if (GSS_ERROR(major))
447             return major;
448     }
449
450     if (cred->expiryTime == 0) {
451         lifetime = GSS_C_INDEFINITE;
452     } else  {
453         now = time(NULL);
454         lifetime = now - cred->expiryTime;
455         if (lifetime < 0)
456             lifetime = 0;
457     }
458
459     if (pLifetime != NULL) {
460         *pLifetime = lifetime;
461     }
462
463     if (lifetime == 0) {
464         *minor = GSSEAP_CRED_EXPIRED;
465         return GSS_S_CREDENTIALS_EXPIRED;
466     }
467
468     major = GSS_S_COMPLETE;
469     *minor = 0;
470
471     return major;
472 }