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