Krb5 ticket support for server-to-server and localauth
[openafs.git] / src / auth / authcon.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #ifdef IGNORE_SOME_GCC_WARNINGS
17 # pragma GCC diagnostic warning "-Wdeprecated-declarations"
18 #endif
19
20 #define HC_DEPRECATED
21 #include <hcrypto/des.h>
22 #include <hcrypto/rand.h>
23
24 #include <rx/rxkad.h>
25 #include <rx/rx.h>
26
27 #include <afs/pthread_glock.h>
28
29 #include "cellconfig.h"
30 #include "keys.h"
31 #include "ktc.h"
32 #include "auth.h"
33
34 /* return a null security object if nothing else can be done */
35 static afs_int32
36 QuickAuth(struct rx_securityClass **astr, afs_int32 *aindex)
37 {
38     struct rx_securityClass *tc;
39     tc = rxnull_NewClientSecurityObject();
40     *astr = tc;
41     *aindex = RX_SECIDX_NULL;
42     return 0;
43 }
44
45 #if !defined(UKERNEL)
46 static int _afsconf_GetRxkadKrb5Key(void *arock, int kvno, int enctype, void *outkey,
47                                     size_t *keylen)
48 {
49     struct afsconf_dir *adir = arock;
50     struct afsconf_typedKey *kobj;
51     struct rx_opaque *keymat;
52     afsconf_keyType tktype;
53     int tkvno, tenctype;
54     int code;
55
56     code = afsconf_GetKeyByTypes(adir, afsconf_rxkad_krb5, kvno, enctype, &kobj);
57     if (code != 0)
58         return code;
59     afsconf_typedKey_values(kobj, &tktype, &tkvno, &tenctype, &keymat);
60     if (*keylen < keymat->len) {
61         afsconf_typedKey_put(&kobj);
62         return AFSCONF_BADKEY;
63     }
64     memcpy(outkey, keymat->val, keymat->len);
65     *keylen = keymat->len;
66     afsconf_typedKey_put(&kobj);
67     return 0;
68 }
69
70
71 /* Return an appropriate security class and index */
72 afs_int32
73 afsconf_ServerAuth(void *arock,
74                    struct rx_securityClass **astr,
75                    afs_int32 *aindex)
76 {
77     struct afsconf_dir *adir = (struct afsconf_dir *) arock;
78     struct rx_securityClass *tclass;
79
80     LOCK_GLOBAL_MUTEX;
81     tclass = (struct rx_securityClass *)
82         rxkad_NewKrb5ServerSecurityObject(0, adir, afsconf_GetKey,
83                                           _afsconf_GetRxkadKrb5Key, NULL);
84     if (tclass) {
85         *astr = tclass;
86         *aindex = RX_SECIDX_KAD;
87         UNLOCK_GLOBAL_MUTEX;
88         return 0;
89     } else {
90         UNLOCK_GLOBAL_MUTEX;
91         return 2;
92     }
93 }
94 #endif /* !defined(UKERNEL) */
95
96 static afs_int32
97 GenericAuth(struct afsconf_dir *adir,
98             struct rx_securityClass **astr,
99             afs_int32 *aindex,
100             rxkad_level enclevel)
101 {
102 #ifdef UKERNEL
103     return QuickAuth(astr, aindex);
104 #else
105     int enctype_preflist[]={18, 17, 23, 16, 0};
106     char tbuffer[512];
107     struct ktc_encryptionKey key, session;
108     struct rx_securityClass *tclass;
109     afs_int32 kvno;
110     afs_int32 ticketLen;
111     afs_int32 code;
112     int use_krb5=0;
113     struct afsconf_typedKey *kobj;
114     struct rx_opaque *keymat;
115     int *et;
116
117     /* first, find the right key and kvno to use */
118
119     et = enctype_preflist;
120     while(*et != 0) {
121         code = afsconf_GetLatestKeyByTypes(adir, afsconf_rxkad_krb5, *et,
122                                            &kobj);
123         if (code == 0) {
124             afsconf_keyType tktype;
125             int tenctype;
126             afsconf_typedKey_values(kobj, &tktype, &kvno, &tenctype, &keymat);
127             RAND_add(keymat->val, keymat->len, 0.0);
128             use_krb5 = 1;
129             break;
130         }
131         et++;
132     }
133
134     if (use_krb5 == 0) {
135         code = afsconf_GetLatestKey(adir, &kvno, &key);
136         if (code) {
137             return QuickAuth(astr, aindex);
138         }
139         /* next create random session key, using key for seed to good random */
140         DES_init_random_number_generator((DES_cblock *) &key);
141     }
142     code = DES_new_random_key((DES_cblock *) &session);
143     if (code) {
144         if (use_krb5)
145             afsconf_typedKey_put(&kobj);
146         return QuickAuth(astr, aindex);
147     }
148
149     if (use_krb5) {
150         ticketLen = sizeof(tbuffer);
151         memset(tbuffer, '\0', sizeof(tbuffer));
152         code =
153             tkt_MakeTicket5(tbuffer, &ticketLen, *et, &kvno, keymat->val,
154                             keymat->len, AUTH_SUPERUSER, "", "", 0, 0x7fffffff,
155                             &session, "afs", "");
156         afsconf_typedKey_put(&kobj);
157     } else {
158         /* now create the actual ticket */
159         ticketLen = sizeof(tbuffer);
160         memset(tbuffer, '\0', sizeof(tbuffer));
161         code =
162             tkt_MakeTicket(tbuffer, &ticketLen, &key, AUTH_SUPERUSER, "", "", 0,
163                            0xffffffff, &session, 0, "afs", "");
164         /* parms were buffer, ticketlen, key to seal ticket with, principal
165          * name, instance and cell, start time, end time, session key to seal
166          * in ticket, inet host, server name and server instance */
167     }
168     if (code) {
169         return QuickAuth(astr, aindex);
170     }
171
172     /* Next, we have ticket, kvno and session key, authenticate the connection.
173      * We use a magic # instead of a constant because of basic compilation
174      * order when compiling the system from scratch (rx/rxkad.h isn't installed
175      * yet). */
176     tclass = (struct rx_securityClass *)
177         rxkad_NewClientSecurityObject(enclevel, &session, kvno, ticketLen,
178                                       tbuffer);
179     *astr = tclass;
180     *aindex = RX_SECIDX_KAD;
181     return 0;
182 #endif
183 }
184
185 /* build a fake ticket for 'afs' using keys from adir, returning an
186  * appropriate security class and index
187  */
188 afs_int32
189 afsconf_ClientAuth(void *arock, struct rx_securityClass ** astr,
190                    afs_int32 * aindex)
191 {
192     struct afsconf_dir * adir = (struct afsconf_dir *) arock;
193     afs_int32 rc;
194
195     LOCK_GLOBAL_MUTEX;
196     rc = GenericAuth(adir, astr, aindex, rxkad_clear);
197     UNLOCK_GLOBAL_MUTEX;
198     return rc;
199 }
200
201 /* build a fake ticket for 'afs' using keys from adir, returning an
202  * appropriate security class and index.  This one, unlike the above,
203  * tells rxkad to encrypt the data, too.
204  */
205 afs_int32
206 afsconf_ClientAuthSecure(void *arock,
207                          struct rx_securityClass **astr,
208                          afs_int32 *aindex)
209 {
210     struct afsconf_dir *adir = (struct afsconf_dir *) arock;
211     afs_int32 rc;
212
213     LOCK_GLOBAL_MUTEX;
214     rc = GenericAuth(adir, astr, aindex, rxkad_crypt);
215     UNLOCK_GLOBAL_MUTEX;
216     return rc;
217 }
218
219 /*!
220  * Build a security class from the user's current tokens
221  *
222  * This function constructs an RX security class from a user's current
223  * tokens.
224  *
225  * @param[in] info      The cell information structure
226  * @param[in] flags     Security flags describing the desired mechanism
227  * @param[out] sc       The selected security class
228  * @param[out] scIndex  The index of the selected class
229  * @parma[out] expires  The expiry time of the tokens used to build the class
230  *
231  * Only the AFSCONF_SECOPTS_ALWAYSENCRYPT flag will modify the behaviour of
232  * this function - it determines whether a cleartext, or encrypting, security
233  * class is provided.
234  *
235  * @return
236  *     0 on success, non-zero on failure. An error code of
237  *     AFSCONF_NO_SECURITY_CLASS indicates that were were unable to build a
238  *     security class using the selected tokens.
239  */
240
241 afs_int32
242 afsconf_ClientAuthToken(struct afsconf_cell *info,
243                         afsconf_secflags flags,
244                         struct rx_securityClass **sc,
245                         afs_int32 *scIndex,
246                         time_t *expires)
247 {
248     struct ktc_setTokenData *tokenSet = NULL;
249     struct ktc_token ttoken;
250     int encryptLevel;
251     afs_int32 code;
252
253     *sc = NULL;
254     *scIndex = RX_SECIDX_NULL;
255
256     code = ktc_GetTokenEx(info->name, &tokenSet);
257     if (code)
258         goto out;
259
260     code = token_extractRxkad(tokenSet, &ttoken, NULL, NULL);
261     if (code == 0) {
262         /* XXX - We should think about how to handle this */
263         if (ttoken.kvno < 0 || ttoken.kvno > 256) {
264              fprintf(stderr,
265                     "funny kvno (%d) in ticket, proceeding\n",
266                     ttoken.kvno);
267         }
268         if (flags & AFSCONF_SECOPTS_ALWAYSENCRYPT)
269             encryptLevel = rxkad_crypt;
270         else
271             encryptLevel = rxkad_clear;
272         *sc = rxkad_NewClientSecurityObject(encryptLevel,
273                                             &ttoken.sessionKey,
274                                             ttoken.kvno,
275                                             ttoken.ticketLen,
276                                             ttoken.ticket);
277         *scIndex = RX_SECIDX_KAD;
278         if (expires)
279             *expires = ttoken.endTime;
280     }
281
282 out:
283     token_FreeSet(&tokenSet);
284
285     if (*sc == NULL)
286         return AFSCONF_NO_SECURITY_CLASS;
287
288     return code;
289 }
290
291 /*!
292  * Set the security flags to be used for a particular configuration
293  */
294 void
295 afsconf_SetSecurityFlags(struct afsconf_dir *dir,
296                          afsconf_secflags flags)
297 {
298     dir->securityFlags = flags;
299 }
300
301 /*!
302  * Build a set of security classes suitable for a server accepting
303  * incoming connections
304  */
305 #if !defined(UKERNEL)
306 void
307 afsconf_BuildServerSecurityObjects(void *rock,
308                                    struct rx_securityClass ***classes,
309                                    afs_int32 *numClasses)
310 {
311     struct afsconf_dir *dir = rock;
312
313     if (dir->securityFlags & AFSCONF_SECOPTS_ALWAYSENCRYPT)
314         *numClasses = 4;
315     else
316         *numClasses = 3;
317
318     *classes = calloc(*numClasses, sizeof(**classes));
319
320     (*classes)[0] = rxnull_NewServerSecurityObject();
321     (*classes)[1] = NULL;
322     (*classes)[2] = rxkad_NewKrb5ServerSecurityObject(0, dir,
323                                                       afsconf_GetKey,
324                                                       _afsconf_GetRxkadKrb5Key,
325                                                       NULL);
326
327     if (dir->securityFlags & AFSCONF_SECOPTS_ALWAYSENCRYPT)
328         (*classes)[3] = rxkad_NewKrb5ServerSecurityObject(rxkad_crypt, dir,
329                                                           afsconf_GetKey,
330                                                           _afsconf_GetRxkadKrb5Key,
331                                                           NULL);
332 }
333 #endif
334
335 /*!
336  * Pick a security class to use for an outgoing connection
337  *
338  * This function selects an RX security class to use for an outgoing
339  * connection, based on the set of security flags provided.
340  *
341  * @param[in] dir
342  *      The configuration directory structure for this cell. If NULL,
343  *      no classes requiring local configuration will be returned.
344  * @param[in] flags
345  *      A set of flags to determine the properties of the security class which
346  *      is selected
347  *      - AFSCONF_SECOPTS_NOAUTH - return an anonymous secirty class
348  *      - AFSCONF_SECOPTS_LOCALAUTH - use classes which have local key
349  *              material available.
350  *      - AFSCONF_SECOPTS_ALWAYSENCRYPT - use classes in encrypting, rather
351  *              than authentication or integrity modes.
352  *      - AFSCONF_SECOPTS_FALLBACK_NULL - if no suitable class can be found,
353  *              then fallback to the rxnull security class.
354  * @param[in] info
355  *      The cell information structure for the current cell. If this is NULL,
356  *      then use a version locally obtained using the cellName.
357  * @param[in] cellName
358  *      The cellName to use when obtaining cell information (may be NULL if
359  *      info is specified)
360  * @param[out] sc
361  *      The selected security class
362  * @param[out] scIndex
363  *      The index of the selected security class
364  * @param[out] expires
365  *      The expiry time of the tokens used to construct the class. Will be
366  *      NEVER_DATE if the class has an unlimited lifetime. If NULL, the
367  *      function won't store the expiry date.
368  *
369  * @return
370  *      Returns 0 on success, or a com_err error code on failure.
371  */
372 afs_int32
373 afsconf_PickClientSecObj(struct afsconf_dir *dir, afsconf_secflags flags,
374                          struct afsconf_cell *info,
375                          char *cellName, struct rx_securityClass **sc,
376                          afs_int32 *scIndex, time_t *expires) {
377     struct afsconf_cell localInfo;
378     afs_int32 code = 0;
379
380     *sc = NULL;
381     *scIndex = RX_SECIDX_NULL;
382     if (expires)
383         *expires = 0;
384
385     if ( !(flags & AFSCONF_SECOPTS_NOAUTH) ) {
386         if (!dir)
387             return AFSCONF_NOCELLDB;
388
389         if (flags & AFSCONF_SECOPTS_LOCALAUTH) {
390             if (flags & AFSCONF_SECOPTS_ALWAYSENCRYPT)
391                 code = afsconf_ClientAuthSecure(dir, sc, scIndex);
392             else
393                 code = afsconf_ClientAuth(dir, sc, scIndex);
394
395             if (code)
396                 goto out;
397
398             /* The afsconf_ClientAuth functions will fall back to giving
399              * a rxnull object, which we don't want if localauth has been
400              * explicitly requested. Check for this, and bail out if we
401              * get one. Note that this leaks a security object at present
402              */
403             if (!(flags & AFSCONF_SECOPTS_FALLBACK_NULL) &&
404                 *scIndex == RX_SECIDX_NULL) {
405                 sc = NULL;
406                 code = AFSCONF_NOTFOUND;
407                 goto out;
408             }
409
410             if (expires)
411                 *expires = NEVERDATE;
412         } else {
413             if (info == NULL) {
414                 code = afsconf_GetCellInfo(dir, cellName, NULL, &localInfo);
415                 if (code)
416                     goto out;
417                 info = &localInfo;
418             }
419
420             code = afsconf_ClientAuthToken(info, flags, sc, scIndex, expires);
421             if (code && !(flags & AFSCONF_SECOPTS_FALLBACK_NULL))
422                 goto out;
423
424             /* If we didn't get a token, we'll just run anonymously */
425             code = 0;
426         }
427     }
428     if (*sc == NULL) {
429         *sc = rxnull_NewClientSecurityObject();
430         *scIndex = RX_SECIDX_NULL;
431         if (expires)
432             *expires = NEVERDATE;
433     }
434
435 out:
436     return code;
437 }