Commit | Line | Data |
---|---|---|
805e021f CE |
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 | } |