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