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