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