2 * Copyright 2000, International Business Machines Corporation and others.
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
10 /* The rxkad security object. Authentication using a DES-encrypted
11 * Kerberos-style ticket. These are the server-only routines. */
14 #include <afsconfig.h>
15 #include <afs/param.h>
20 #include <sys/types.h>
25 #include <netinet/in.h>
37 #include <afs/afsutil.h>
38 #include "private_data.h"
39 #define XPRT_RXKAD_SERVER
42 * This can be set to allow alternate ticket decoding.
43 * Currently only used by the AFS/DFS protocol translator to recognize
44 * Kerberos V5 tickets. The actual code to do that is provided externally.
46 afs_int32 (*rxkad_AlternateTicketDecoder)();
48 static struct rx_securityOps rxkad_server_ops = {
51 rxkad_PreparePacket, /* once per packet creation */
52 0, /* send packet (once per retrans) */
53 rxkad_CheckAuthentication,
54 rxkad_CreateChallenge,
58 rxkad_CheckPacket, /* check data packet */
59 rxkad_DestroyConnection,
62 extern afs_uint32 rx_MyMaxSendSize;
64 /* Miscellaneous random number routines that use the fcrypt module and the
67 static fc_KeySchedule random_int32_schedule;
69 #ifdef AFS_PTHREAD_ENV
71 * This mutex protects the following global variables:
72 * random_int32_schedule
77 pthread_mutex_t rxkad_random_mutex;
78 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0);
79 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0);
83 #endif /* AFS_PTHREAD_ENV */
85 static void init_random_int32 (void)
88 gettimeofday (&key, NULL);
90 fc_keysched (&key, random_int32_schedule);
94 static afs_int32 get_random_int32 (void)
95 { static struct timeval seed;
99 fc_ecb_encrypt (&seed, &seed, random_int32_schedule, ENCRYPT);
105 /* Called with four parameters. The first is the level of encryption, as
106 defined in the rxkad.h file. The second and third are a rock and a
107 procedure that is called with the key version number that accompanies the
108 ticket and returns a pointer to the server's decryption key. The fourth
109 argument, if not NULL, is a pointer to a function that will be called for
110 every new connection with the name, instance and cell of the client. The
111 routine should return zero if the user is NOT acceptible to the server. If
112 this routine is not supplied, the server can call rxkad_GetServerInfo with
113 the rx connection pointer passed to the RPC routine to obtain information
117 rxkad_level level; * minimum level *
118 char *get_key_rock; * rock for get_key implementor *
119 int (*get_key)(); * passed kvno & addr(key) to fill *
120 int (*user_ok)(); * passed name, inst, cell => bool *
123 struct rx_securityClass *rxkad_NewServerSecurityObject (
124 rxkad_level level, char *get_key_rock,
125 int (*get_key)(char *get_key_rock, int kvno, struct ktc_encryptionKey *serverKey),
126 int (*user_ok)(char *name, char *instance, char *cell, afs_int32 kvno))
127 { struct rx_securityClass *tsc;
128 struct rxkad_sprivate *tsp;
131 if (!get_key) return 0;
133 size = sizeof(struct rx_securityClass);
134 tsc = (struct rx_securityClass *) osi_Alloc (size);
135 memset(tsc, 0, size);
136 tsc->refCount = 1; /* caller has one reference */
137 tsc->ops = &rxkad_server_ops;
138 size = sizeof(struct rxkad_sprivate);
139 tsp = (struct rxkad_sprivate *) osi_Alloc (size);
140 memset(tsp, 0, size);
141 tsc->privateData = (char *) tsp;
143 tsp->type |= rxkad_server; /* so can identify later */
144 tsp->level = level; /* level of encryption */
145 tsp->get_key_rock = get_key_rock;
146 tsp->get_key = get_key; /* to get server ticket */
147 tsp->user_ok = user_ok; /* to inform server of client id. */
148 init_random_int32 ();
151 rxkad_stats_serverObjects++;
156 /* server: called to tell if a connection authenticated properly */
158 int rxkad_CheckAuthentication (struct rx_securityClass *aobj,
159 struct rx_connection *aconn)
160 { struct rxkad_sconn *sconn;
162 /* first make sure the object exists */
163 if (!aconn->securityData) return RXKADINCONSISTENCY;
165 sconn = (struct rxkad_sconn *) aconn->securityData;
166 return !sconn->authenticated;
169 /* server: put the current challenge in the connection structure for later use
172 int rxkad_CreateChallenge(struct rx_securityClass *aobj,
173 struct rx_connection *aconn)
174 { struct rxkad_sconn *sconn;
175 struct rxkad_sprivate *tsp;
177 sconn = (struct rxkad_sconn *) aconn->securityData;
178 sconn->challengeID = get_random_int32 ();
179 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
180 /* initialize level from object's minimum acceptable level */
181 tsp = (struct rxkad_sprivate *)aobj->privateData;
182 sconn->level = tsp->level;
186 /* server: fill in a challenge in the packet */
188 int rxkad_GetChallenge (struct rx_securityClass *aobj,
189 struct rx_connection *aconn, struct rx_packet *apacket)
190 { struct rxkad_sconn *sconn;
193 struct rxkad_v2Challenge c_v2; /* version 2 */
194 struct rxkad_oldChallenge c_old; /* old style */
196 sconn = (struct rxkad_sconn *) aconn->securityData;
197 if (rx_IsUsingPktCksum(aconn)) sconn->cksumSeen = 1;
199 if (sconn->cksumSeen) {
200 memset(&c_v2, 0, sizeof(c_v2));
201 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
202 c_v2.challengeID = htonl(sconn->challengeID);
203 c_v2.level = htonl((afs_int32)sconn->level);
205 challenge = (char *)&c_v2;
206 challengeSize = sizeof(c_v2);
208 memset(&c_old, 0, sizeof(c_old));
209 c_old.challengeID = htonl(sconn->challengeID);
210 c_old.level = htonl((afs_int32)sconn->level);
211 challenge = (char *)&c_old;
212 challengeSize = sizeof(c_old);
214 if (rx_MyMaxSendSize < challengeSize)
215 return RXKADPACKETSHORT; /* not enough space */
217 rx_packetwrite(apacket, 0, challengeSize, challenge);
218 rx_SetDataSize (apacket, challengeSize);
221 rxkad_stats.challengesSent++;
226 /* server: process a response to a challenge packet */
227 /* XXX this does some copying of data in and out of the packet, but I'll bet it
228 * could just do it in place, especially if I used rx_Pullup...
230 int rxkad_CheckResponse (struct rx_securityClass *aobj,
231 struct rx_connection *aconn, struct rx_packet *apacket)
232 { struct rxkad_sconn *sconn;
233 struct rxkad_sprivate *tsp;
234 struct ktc_encryptionKey serverKey;
235 struct rxkad_oldChallengeResponse oldr; /* response format */
236 struct rxkad_v2ChallengeResponse v2r;
237 afs_int32 tlen; /* ticket len */
238 afs_int32 kvno; /* key version of ticket */
239 char tix[MAXKTCTICKETLEN];
240 afs_int32 incChallengeID;
243 /* ticket contents */
244 struct ktc_principal client;
245 struct ktc_encryptionKey sessionkey;
250 struct rxkad_serverinfo *rock;
252 sconn = (struct rxkad_sconn *) aconn->securityData;
253 tsp = (struct rxkad_sprivate *) aobj->privateData;
255 if (sconn->cksumSeen) {
256 /* expect v2 response, leave fields in v2r in network order for cksum
257 * computation which follows decryption. */
258 if (rx_GetDataSize(apacket) < sizeof(v2r))
259 return RXKADPACKETSHORT;
260 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
264 kvno = ntohl (v2r.kvno);
265 tlen = ntohl(v2r.ticketLen);
266 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
267 return RXKADPACKETSHORT;
269 /* expect old format response */
270 if (rx_GetDataSize(apacket) < sizeof(oldr)) return RXKADPACKETSHORT;
271 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
274 kvno = ntohl (oldr.kvno);
275 tlen = ntohl(oldr.ticketLen);
276 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
277 return RXKADPACKETSHORT;
279 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
280 return RXKADTICKETLEN;
282 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
285 * We allow the ticket to be optionally decoded by an alternate
286 * ticket decoder, if the function variable
287 * rxkad_AlternateTicketDecoder is set. That function should
288 * return a code of -1 if it wants the ticket to be decoded by
289 * the standard decoder.
291 if (rxkad_AlternateTicketDecoder) {
292 code = rxkad_AlternateTicketDecoder
293 (kvno, tix, tlen, client.name, client.instance, client.cell,
294 &sessionkey, &host, &start, &end);
295 if (code && code != -1) {
299 code = -1; /* No alternate ticket decoder present */
303 * If the alternate decoder is not present, or returns -1, then
304 * assume the ticket is of the default style.
307 /* get ticket's key */
308 code = (*tsp->get_key)(tsp->get_key_rock, kvno, &serverKey);
309 if (code) return RXKADUNKNOWNKEY; /* invalid kvno */
310 code = tkt_DecodeTicket (tix, tlen, &serverKey,
311 client.name, client.instance, client.cell,
312 &sessionkey, &host, &start, &end);
313 if (code) return RXKADBADTICKET;
315 code = tkt_CheckTimes (start, end, time(0));
316 if (code == -1) return RXKADEXPIRED;
317 else if (code <= 0) return RXKADNOAUTH;
319 code = fc_keysched (&sessionkey, sconn->keysched);
320 if (code) return RXKADBADKEY;
321 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
323 if (sconn->cksumSeen) {
324 /* using v2 response */
325 afs_uint32 cksum; /* observed cksum */
326 struct rxkad_endpoint endpoint; /* connections endpoint */
330 memcpy(xor, sconn->ivec, 2*sizeof(afs_int32));
331 fc_cbc_encrypt (&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
332 sconn->keysched, xor, DECRYPT);
333 cksum = rxkad_CksumChallengeResponse (&v2r);
334 if (cksum != v2r.encrypted.endpoint.cksum)
335 return RXKADSEALEDINCON;
336 (void) rxkad_SetupEndpoint (aconn, &endpoint);
337 v2r.encrypted.endpoint.cksum = 0;
338 if (memcmp (&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
339 return RXKADSEALEDINCON;
340 for (i=0; i<RX_MAXCALLS; i++) {
341 v2r.encrypted.callNumbers[i] = ntohl(v2r.encrypted.callNumbers[i]);
342 if (v2r.encrypted.callNumbers[i] < 0) return RXKADSEALEDINCON;
345 (void) rxi_SetCallNumberVector (aconn, v2r.encrypted.callNumbers);
346 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
347 level = ntohl(v2r.encrypted.level);
349 /* expect old format response */
350 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted,
351 sconn->keysched, DECRYPT);
352 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
353 level = ntohl(oldr.encrypted.level);
355 if (incChallengeID != sconn->challengeID+1)
356 return RXKADOUTOFSEQUENCE; /* replay attempt */
357 if ((level < sconn->level) || (level > rxkad_crypt)) return RXKADLEVELFAIL;
358 sconn->level = level;
359 rxkad_SetLevel (aconn, sconn->level);
361 rxkad_stats.responses[rxkad_LevelIndex(sconn->level)]++;
364 /* now compute endpoint-specific info used for computing 16 bit checksum */
365 rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec, sconn->preSeq);
367 /* otherwise things are ok */
368 sconn->expirationTime = end;
369 sconn->authenticated = 1;
372 code = tsp->user_ok (client.name, client.instance, client.cell, kvno);
373 if (code) return RXKADNOAUTH;
375 else { /* save the info for later retreival */
376 int size = sizeof(struct rxkad_serverinfo);
377 rock = (struct rxkad_serverinfo *) osi_Alloc (size);
378 memset(rock, 0, size);
380 memcpy(&rock->client, &client, sizeof(rock->client));
386 /* return useful authentication info about a server-side connection */
388 afs_int32 rxkad_GetServerInfo (struct rx_connection *aconn,
389 rxkad_level *level, afs_uint32 *expiration, char *name, char *instance,
390 char *cell, afs_int32 *kvno)
392 struct rxkad_sconn *sconn;
394 sconn = (struct rxkad_sconn *) aconn->securityData;
395 if (sconn && sconn->authenticated && sconn->rock &&
396 (time(0) < sconn->expirationTime)) {
397 if (level) *level = sconn->level;
398 if (expiration) *expiration = sconn->expirationTime;
399 if (name) strcpy (name, sconn->rock->client.name);
400 if (instance) strcpy (instance, sconn->rock->client.instance);
401 if (cell) strcpy (cell, sconn->rock->client.cell);
402 if (kvno) *kvno = sconn->rock->kvno;
405 else return RXKADNOAUTH;