Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / rxkad / rxkad_server.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 /* The rxkad security object. Authentication using a DES-encrypted
11 * Kerberos-style ticket. These are the server-only routines. */
12
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16 #include <afs/stds.h>
17
18 #include <roken.h>
19
20 #if (defined(AFS_AIX_ENV) && defined(KERNEL) && !defined(UKERNEL)) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
21 #include <sys/systm.h>
22 #endif
23
24 #include <afs/opr.h>
25 #include <rx/rx.h>
26 #include <rx/xdr.h>
27 #include <rx/rx_packet.h>
28 #include <afs/afsutil.h>
29
30 #include "stats.h"
31 #include "private_data.h"
32 #define XPRT_RXKAD_SERVER
33
34 /*
35 * This can be set to allow alternate ticket decoding.
36 * Currently only used by the AFS/DFS protocol translator to recognize
37 * Kerberos V5 tickets. The actual code to do that is provided externally.
38 */
39 afs_int32(*rxkad_AlternateTicketDecoder) (afs_int32, char *, afs_int32,
40 char *, char *, char *,
41 struct ktc_encryptionKey *,
42 afs_int32 *, afs_uint32 *,
43 afs_uint32 *);
44
45 static struct rx_securityOps rxkad_server_ops = {
46 rxkad_Close,
47 rxkad_NewConnection,
48 rxkad_PreparePacket, /* once per packet creation */
49 0, /* send packet (once per retrans) */
50 rxkad_CheckAuthentication,
51 rxkad_CreateChallenge,
52 rxkad_GetChallenge,
53 0,
54 rxkad_CheckResponse,
55 rxkad_CheckPacket, /* check data packet */
56 rxkad_DestroyConnection,
57 rxkad_GetStats,
58 rxkad_SetConfiguration,
59 0, /* spare 2 */
60 0, /* spare 3 */
61 };
62 extern afs_uint32 rx_MyMaxSendSize;
63
64 /* Miscellaneous random number routines that use the fcrypt module and the
65 * timeofday. */
66
67 static fc_KeySchedule random_int32_schedule;
68
69 #ifdef AFS_PTHREAD_ENV
70 /*
71 * This mutex protects the following global variables:
72 * random_int32_schedule
73 * seed
74 */
75
76 pthread_mutex_t rxkad_random_mutex
77 #ifdef PTHREAD_MUTEX_INITIALIZER
78 = PTHREAD_MUTEX_INITIALIZER
79 #endif
80 ;
81 #define LOCK_RM opr_Verify(pthread_mutex_lock(&rxkad_random_mutex)==0)
82 #define UNLOCK_RM opr_Verify(pthread_mutex_unlock(&rxkad_random_mutex)==0)
83 #else
84 #define LOCK_RM
85 #define UNLOCK_RM
86 #endif /* AFS_PTHREAD_ENV */
87
88 static void
89 init_random_int32(void)
90 {
91 struct timeval key;
92
93 gettimeofday(&key, NULL);
94 LOCK_RM;
95 fc_keysched((struct ktc_encryptionKey*)&key, random_int32_schedule);
96 UNLOCK_RM;
97 }
98
99 static afs_int32
100 get_random_int32(void)
101 {
102 static struct timeval seed;
103 afs_int32 rc;
104
105 LOCK_RM;
106 fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
107 rc = seed.tv_sec;
108 UNLOCK_RM;
109 return rc;
110 }
111
112 /* Called with four parameters. The first is the level of encryption, as
113 defined in the rxkad.h file. The second and third are a rock and a
114 procedure that is called with the key version number that accompanies the
115 ticket and returns a pointer to the server's decryption key. The fourth
116 argument, if not NULL, is a pointer to a function that will be called for
117 every new connection with the name, instance and cell of the client. The
118 routine should return zero if the user is NOT acceptible to the server. If
119 this routine is not supplied, the server can call rxkad_GetServerInfo with
120 the rx connection pointer passed to the RPC routine to obtain information
121 about the client. */
122
123 /*
124 rxkad_level level; * minimum level *
125 char *get_key_rock; * rock for get_key implementor *
126 int (*get_key)(); * passed kvno & addr(key) to fill *
127 int (*user_ok)(); * passed name, inst, cell => bool *
128 */
129
130 struct rx_securityClass *
131 rxkad_NewServerSecurityObject(rxkad_level level, void *get_key_rock,
132 int (*get_key) (void *get_key_rock, int kvno,
133 struct ktc_encryptionKey *
134 serverKey),
135 int (*user_ok) (char *name, char *instance,
136 char *cell, afs_int32 kvno))
137 {
138 struct rx_securityClass *tsc;
139 struct rxkad_sprivate *tsp;
140 int size;
141
142 rxkad_Init();
143
144 if (!get_key)
145 return 0;
146
147 size = sizeof(struct rx_securityClass);
148 tsc = rxi_Alloc(size);
149 memset(tsc, 0, size);
150 tsc->refCount = 1; /* caller has one reference */
151 tsc->ops = &rxkad_server_ops;
152 size = sizeof(struct rxkad_sprivate);
153 tsp = rxi_Alloc(size);
154 memset(tsp, 0, size);
155 tsc->privateData = (char *)tsp;
156
157 tsp->type |= rxkad_server; /* so can identify later */
158 tsp->level = level; /* level of encryption */
159 tsp->get_key_rock = get_key_rock;
160 tsp->get_key = get_key; /* to get server ticket */
161 tsp->user_ok = user_ok; /* to inform server of client id. */
162 init_random_int32();
163
164 INC_RXKAD_STATS(serverObjects);
165 return tsc;
166 }
167
168 struct rx_securityClass *
169 rxkad_NewKrb5ServerSecurityObject(rxkad_level level, void *get_key_rock,
170 int (*get_key) (void *get_key_rock, int kvno,
171 struct ktc_encryptionKey *
172 serverKey),
173 rxkad_get_key_enctype_func get_key_enctype,
174 int (*user_ok) (char *name, char *instance,
175 char *cell, afs_int32 kvno)
176 ) {
177 struct rx_securityClass *tsc;
178 struct rxkad_sprivate *tsp;
179 tsc = rxkad_NewServerSecurityObject(level, get_key_rock, get_key, user_ok);
180 tsp = (struct rxkad_sprivate *)tsc->privateData;
181 tsp->get_key_enctype = get_key_enctype;
182 return tsc;
183 }
184
185 /* server: called to tell if a connection authenticated properly */
186
187 int
188 rxkad_CheckAuthentication(struct rx_securityClass *aobj,
189 struct rx_connection *aconn)
190 {
191 struct rxkad_sconn *sconn = rx_GetSecurityData(aconn);
192
193 /* first make sure the object exists */
194 if (!sconn)
195 return RXKADINCONSISTENCY;
196
197 return !sconn->authenticated;
198 }
199
200 /* server: put the current challenge in the connection structure for later use
201 by packet sender */
202
203 int
204 rxkad_CreateChallenge(struct rx_securityClass *aobj,
205 struct rx_connection *aconn)
206 {
207 struct rxkad_sconn *sconn = rx_GetSecurityData(aconn);
208 struct rxkad_sprivate *tsp;
209
210 sconn->challengeID = get_random_int32();
211 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
212 /* initialize level from object's minimum acceptable level */
213 tsp = (struct rxkad_sprivate *)aobj->privateData;
214 sconn->level = tsp->level;
215 return 0;
216 }
217
218 /* server: fill in a challenge in the packet */
219
220 int
221 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
222 struct rx_packet *apacket)
223 {
224 struct rxkad_sconn *sconn = rx_GetSecurityData(aconn);
225 char *challenge;
226 int challengeSize;
227 struct rxkad_v2Challenge c_v2; /* version 2 */
228 struct rxkad_oldChallenge c_old; /* old style */
229
230 if (rx_IsUsingPktCksum(aconn))
231 sconn->cksumSeen = 1;
232
233 if (sconn->cksumSeen) {
234 memset(&c_v2, 0, sizeof(c_v2));
235 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
236 c_v2.challengeID = htonl(sconn->challengeID);
237 c_v2.level = htonl((afs_int32) sconn->level);
238 c_v2.spare = 0;
239 challenge = (char *)&c_v2;
240 challengeSize = sizeof(c_v2);
241 } else {
242 memset(&c_old, 0, sizeof(c_old));
243 c_old.challengeID = htonl(sconn->challengeID);
244 c_old.level = htonl((afs_int32) sconn->level);
245 challenge = (char *)&c_old;
246 challengeSize = sizeof(c_old);
247 }
248 if (rx_MyMaxSendSize < challengeSize)
249 return RXKADPACKETSHORT; /* not enough space */
250
251 rx_packetwrite(apacket, 0, challengeSize, challenge);
252 rx_SetDataSize(apacket, challengeSize);
253 sconn->tried = 1;
254 INC_RXKAD_STATS(challengesSent);
255 return 0;
256 }
257
258 /* server: process a response to a challenge packet */
259 /* XXX this does some copying of data in and out of the packet, but I'll bet it
260 * could just do it in place, especially if I used rx_Pullup...
261 */
262 int
263 rxkad_CheckResponse(struct rx_securityClass *aobj,
264 struct rx_connection *aconn, struct rx_packet *apacket)
265 {
266 struct rxkad_sconn *sconn;
267 struct rxkad_sprivate *tsp;
268 struct ktc_encryptionKey serverKey;
269 struct rxkad_oldChallengeResponse oldr; /* response format */
270 struct rxkad_v2ChallengeResponse v2r;
271 afs_int32 tlen; /* ticket len */
272 afs_int32 kvno; /* key version of ticket */
273 char tix[MAXKTCTICKETLEN];
274 afs_int32 incChallengeID;
275 rxkad_level level;
276 int code;
277 /* ticket contents */
278 struct ktc_principal client;
279 struct ktc_encryptionKey sessionkey;
280 afs_int32 host;
281 afs_uint32 start;
282 afs_uint32 end;
283 unsigned int pos;
284 struct rxkad_serverinfo *rock;
285
286 sconn = rx_GetSecurityData(aconn);
287 tsp = (struct rxkad_sprivate *)aobj->privateData;
288
289 if (sconn->cksumSeen) {
290 /* expect v2 response, leave fields in v2r in network order for cksum
291 * computation which follows decryption. */
292 if (rx_GetDataSize(apacket) < sizeof(v2r))
293 return RXKADPACKETSHORT;
294 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
295 pos = sizeof(v2r);
296 /* version == 2 */
297 /* ignore spare */
298 kvno = ntohl(v2r.kvno);
299 tlen = ntohl(v2r.ticketLen);
300 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
301 return RXKADPACKETSHORT;
302 } else {
303 /* expect old format response */
304 if (rx_GetDataSize(apacket) < sizeof(oldr))
305 return RXKADPACKETSHORT;
306 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
307 pos = sizeof(oldr);
308
309 kvno = ntohl(oldr.kvno);
310 tlen = ntohl(oldr.ticketLen);
311 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
312 return RXKADPACKETSHORT;
313 }
314 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
315 return RXKADTICKETLEN;
316
317 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
318
319 /*
320 * We allow the ticket to be optionally decoded by an alternate
321 * ticket decoder, if the function variable
322 * rxkad_AlternateTicketDecoder is set. That function should
323 * return a code of -1 if it wants the ticket to be decoded by
324 * the standard decoder.
325 */
326 if (rxkad_AlternateTicketDecoder) {
327 code =
328 rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
329 client.instance, client.cell,
330 &sessionkey, &host, &start, &end);
331 if (code && code != -1) {
332 return code;
333 }
334 } else {
335 code = -1; /* No alternate ticket decoder present */
336 }
337
338 /*
339 * If the alternate decoder is not present, or returns -1, then
340 * assume the ticket is of the default style.
341 */
342 if (code == -1 && ((kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
343 || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY))) {
344 code =
345 tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_enctype,
346 tsp->get_key_rock, kvno, client.name,
347 client.instance, client.cell,
348 &sessionkey, &host, &start, &end,
349 tsp->flags & RXS_CONFIG_FLAGS_DISABLE_DOTCHECK);
350 if (code)
351 return code;
352 }
353
354 /*
355 * If the alternate decoder/kerberos 5 decoder is not present, or
356 * returns -1, then assume the ticket is of the default style.
357 */
358 if (code == -1) {
359 /* get ticket's key */
360 code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
361 if (code)
362 return RXKADUNKNOWNKEY; /* invalid kvno */
363 code =
364 tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
365 client.instance, client.cell, &sessionkey, &host,
366 &start, &end);
367 if (code)
368 return code;
369 }
370 code = tkt_CheckTimes(start, end, time(0));
371 if (code == 0)
372 return RXKADNOAUTH;
373 else if (code == -1)
374 return RXKADEXPIRED;
375 else if (code < -1)
376 return RXKADBADTICKET;
377
378 code = fc_keysched(&sessionkey, sconn->keysched);
379 if (code)
380 return RXKADBADKEY;
381 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
382
383 if (sconn->cksumSeen) {
384 /* using v2 response */
385 afs_uint32 cksum; /* observed cksum */
386 struct rxkad_endpoint endpoint; /* connections endpoint */
387 int i;
388 afs_uint32 xor[2];
389
390 memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
391 fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
392 sconn->keysched, xor, DECRYPT);
393 cksum = rxkad_CksumChallengeResponse(&v2r);
394 if (cksum != v2r.encrypted.endpoint.cksum)
395 return RXKADSEALEDINCON;
396 (void)rxkad_SetupEndpoint(aconn, &endpoint);
397 v2r.encrypted.endpoint.cksum = 0;
398 if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
399 return RXKADSEALEDINCON;
400 for (i = 0; i < RX_MAXCALLS; i++) {
401 v2r.encrypted.callNumbers[i] =
402 ntohl(v2r.encrypted.callNumbers[i]);
403 if (v2r.encrypted.callNumbers[i] < 0)
404 return RXKADSEALEDINCON;
405 }
406
407 (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
408 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
409 level = ntohl(v2r.encrypted.level);
410 } else {
411 /* expect old format response */
412 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
413 DECRYPT);
414 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
415 level = ntohl(oldr.encrypted.level);
416 }
417 if (incChallengeID != sconn->challengeID + 1)
418 return RXKADOUTOFSEQUENCE; /* replay attempt */
419 if ((level < sconn->level) || (level > rxkad_crypt))
420 return RXKADLEVELFAIL;
421 sconn->level = level;
422 rxkad_SetLevel(aconn, sconn->level);
423 INC_RXKAD_STATS(responses[rxkad_LevelIndex(sconn->level)]);
424 /* now compute endpoint-specific info used for computing 16 bit checksum */
425 rxkad_DeriveXORInfo(aconn, &sconn->keysched, (char *)sconn->ivec, (char *)sconn->preSeq);
426
427 /* otherwise things are ok */
428 sconn->expirationTime = end;
429 sconn->authenticated = 1;
430
431 if (tsp->user_ok) {
432 code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
433 if (code)
434 return RXKADNOAUTH;
435 } else { /* save the info for later retreival */
436 int size = sizeof(struct rxkad_serverinfo);
437 rock = rxi_Alloc(size);
438 memset(rock, 0, size);
439 rock->kvno = kvno;
440 memcpy(&rock->client, &client, sizeof(rock->client));
441 sconn->rock = rock;
442 }
443 return 0;
444 }
445
446 /* return useful authentication info about a server-side connection */
447
448 afs_int32
449 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
450 afs_uint32 * expiration, char *name, char *instance,
451 char *cell, afs_int32 * kvno)
452 {
453 struct rxkad_sconn *sconn;
454
455 sconn = rx_GetSecurityData(aconn);
456 if (sconn && sconn->authenticated && sconn->rock
457 && (time(0) < sconn->expirationTime)) {
458 if (level)
459 *level = sconn->level;
460 if (expiration)
461 *expiration = sconn->expirationTime;
462 if (name)
463 strcpy(name, sconn->rock->client.name);
464 if (instance)
465 strcpy(instance, sconn->rock->client.instance);
466 if (cell)
467 strcpy(cell, sconn->rock->client.cell);
468 if (kvno)
469 *kvno = sconn->rock->kvno;
470 return 0;
471 } else
472 return RXKADNOAUTH;
473 }
474
475 /* Set security object configuration variables */
476 afs_int32 rxkad_SetConfiguration(struct rx_securityClass *aobj,
477 struct rx_connection *aconn,
478 rx_securityConfigVariables atype,
479 void * avalue, void **currentValue)
480 {
481 struct rxkad_sprivate *private =
482 (struct rxkad_sprivate *) aobj->privateData;
483
484 switch (atype) {
485 case RXS_CONFIG_FLAGS:
486 if (currentValue) {
487 *((afs_uint32 *)currentValue) = private->flags;
488 } else {
489 private->flags = (intptr_t)avalue;
490 }
491 break;
492 default:
493 break;
494 }
495 return 0;
496 }