8db7dd07ecb6ea3f654354276f5f097e794fe1f2
[openafs.git] / src / rxkad / rxkad_client.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 client-only routines.  They do not
12  * make any use of DES. */
13
14 #include <afsconfig.h>
15 #ifdef KERNEL
16 #include "afs/param.h"
17 #else
18 #include <afs/param.h>
19 #endif
20
21 RCSID
22     ("$Header$");
23
24 #ifdef KERNEL
25 #include "afs/stds.h"
26 #ifndef UKERNEL
27 #include "h/types.h"
28 #include "h/time.h"
29 #if defined(AFS_AIX_ENV) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV) 
30 #include "h/systm.h"
31 #endif
32 #ifdef AFS_LINUX20_ENV
33 #include "h/socket.h"
34 #endif
35 #ifndef AFS_OBSD_ENV
36 #include "netinet/in.h"
37 #endif
38 #else /* !UKERNEL */
39 #include "afs/sysincludes.h"
40 #endif /* !UKERNEL */
41 #ifndef AFS_LINUX22_ENV
42 #include "rpc/types.h"
43 #include "rx/xdr.h"
44 #endif
45 #include "rx/rx.h"
46 #else /* ! KERNEL */
47 #include <afs/stds.h>
48 #include <sys/types.h>
49 #include <time.h>
50 #ifdef HAVE_STRING_H
51 #include <string.h>
52 #else
53 #ifdef HAVE_STRINGS_H
54 #include <strings.h>
55 #endif
56 #endif
57 #ifdef AFS_NT40_ENV
58 #include <winsock2.h>
59 #else
60 #include <netinet/in.h>
61 #endif
62 #include <rx/rx.h>
63 #include <rx/xdr.h>
64 #ifdef AFS_PTHREAD_ENV
65 #include "rx/rxkad.h"
66 #endif /* AFS_PTHREAD_ENV */
67 #endif /* KERNEL */
68
69 #include <des/stats.h>
70 #include "private_data.h"
71 #define XPRT_RXKAD_CLIENT
72
73 #ifndef max
74 #define max(a,b)    ((a) < (b)? (b) : (a))
75 #endif /* max */
76
77 static struct rx_securityOps rxkad_client_ops = {
78     rxkad_Close,
79     rxkad_NewConnection,        /* every new connection */
80     rxkad_PreparePacket,        /* once per packet creation */
81     0,                          /* send packet (once per retrans.) */
82     0,
83     0,
84     0,
85     rxkad_GetResponse,          /* respond to challenge packet */
86     0,
87     rxkad_CheckPacket,          /* check data packet */
88     rxkad_DestroyConnection,
89     rxkad_GetStats,
90     0,
91     0,
92     0,
93 };
94
95 /* To minimize changes to epoch, we set this Cuid once, and everyone (including
96  * rxnull) uses it after that.  This means that the Ksession of the first
97  * authencticated connection should be a good one. */
98
99 #ifdef AFS_PTHREAD_ENV
100 /*
101  * This mutex protects the following global variables:
102  * Cuid
103  * counter
104  * rxkad_EpochWasSet
105  */
106 #include <assert.h>
107 pthread_mutex_t rxkad_client_uid_mutex;
108 #define LOCK_CUID assert(pthread_mutex_lock(&rxkad_client_uid_mutex)==0)
109 #define UNLOCK_CUID assert(pthread_mutex_unlock(&rxkad_client_uid_mutex)==0)
110 #else
111 #define LOCK_CUID
112 #define UNLOCK_CUID
113 #endif /* AFS_PTHREAD_ENV */
114
115 static afs_int32 Cuid[2];       /* set once and shared by all */
116 int rxkad_EpochWasSet = 0;      /* TRUE => we called rx_SetEpoch */
117
118 /* allocate a new connetion ID in place */
119 int
120 rxkad_AllocCID(struct rx_securityClass *aobj, struct rx_connection *aconn)
121 {
122     struct rxkad_cprivate *tcp;
123     struct rxkad_cidgen tgen;
124     static afs_int32 counter = 0;       /* not used anymore */
125
126     LOCK_CUID;
127     if (Cuid[0] == 0) {
128         afs_uint32 xor[2];
129         tgen.ipAddr = rxi_getaddr();    /* comes back in net order */
130         clock_GetTime(&tgen.time);      /* changes time1 and time2 */
131         tgen.time.sec = htonl(tgen.time.sec);
132         tgen.time.usec = htonl(tgen.time.usec);
133         tgen.counter = htonl(counter);
134         counter++;
135 #ifdef KERNEL
136         tgen.random1 = afs_random() & 0x7fffffff;       /* was "80000" */
137         tgen.random2 = afs_random() & 0x7fffffff;       /* was "htonl(100)" */
138 #else
139         tgen.random1 = htonl(getpid());
140         tgen.random2 = htonl(100);
141 #endif
142         if (aobj) {
143             /* block is ready for encryption with session key, let's go for it. */
144             tcp = (struct rxkad_cprivate *)aobj->privateData;
145             memcpy((void *)xor, (void *)tcp->ivec, 2 * sizeof(afs_int32));
146             fc_cbc_encrypt((char *)&tgen, (char *)&tgen, sizeof(tgen),
147                            tcp->keysched, xor, ENCRYPT);
148         } else {
149             /* Create a session key so that we can encrypt it */
150
151         }
152         memcpy((void *)Cuid,
153                ((char *)&tgen) + sizeof(tgen) - ENCRYPTIONBLOCKSIZE,
154                ENCRYPTIONBLOCKSIZE);
155         Cuid[0] = (Cuid[0] & ~0x40000000) | 0x80000000;
156         Cuid[1] &= RX_CIDMASK;
157         rx_SetEpoch(Cuid[0]);   /* for future rxnull connections */
158         rxkad_EpochWasSet++;
159     }
160
161     if (!aconn) {
162         UNLOCK_CUID;
163         return 0;
164     }
165     aconn->epoch = Cuid[0];
166     aconn->cid = Cuid[1];
167     Cuid[1] += 1 << RX_CIDSHIFT;
168     UNLOCK_CUID;
169     return 0;
170 }
171
172 /* Allocate a new client security object.  Called with the encryption level,
173  * the session key and the ticket for the other side obtained from the
174  * AuthServer.  Refers to export control to determine level. */
175
176 struct rx_securityClass *
177 rxkad_NewClientSecurityObject(rxkad_level level,
178                               struct ktc_encryptionKey *sessionkey,
179                               afs_int32 kvno, int ticketLen, char *ticket)
180 {
181     struct rx_securityClass *tsc;
182     struct rxkad_cprivate *tcp;
183     int code;
184     int size;
185
186     size = sizeof(struct rx_securityClass);
187     tsc = (struct rx_securityClass *)rxi_Alloc(size);
188     memset((void *)tsc, 0, size);
189     tsc->refCount = 1;          /* caller gets one for free */
190     tsc->ops = &rxkad_client_ops;
191
192     size = sizeof(struct rxkad_cprivate);
193     tcp = (struct rxkad_cprivate *)rxi_Alloc(size);
194     memset((void *)tcp, 0, size);
195     tsc->privateData = (char *)tcp;
196     tcp->type |= rxkad_client;
197     tcp->level = level;
198     code = fc_keysched(sessionkey, tcp->keysched);
199     if (code) {
200         rxi_Free(tcp, sizeof(struct rxkad_cprivate));
201         rxi_Free(tsc, sizeof(struct rx_securityClass));
202         return 0;               /* bad key */
203     }
204     memcpy((void *)tcp->ivec, (void *)sessionkey, sizeof(tcp->ivec));
205     tcp->kvno = kvno;           /* key version number */
206     tcp->ticketLen = ticketLen; /* length of ticket */
207     if (tcp->ticketLen > MAXKTCTICKETLEN) {
208         rxi_Free(tcp, sizeof(struct rxkad_cprivate));
209         rxi_Free(tsc, sizeof(struct rx_securityClass));
210         return 0;               /* bad key */
211     }
212     memcpy(tcp->ticket, ticket, ticketLen);
213
214     INC_RXKAD_STATS(clientObjects);
215     return tsc;
216 }
217
218 /* client: respond to a challenge packet */
219
220 int
221 rxkad_GetResponse(struct rx_securityClass *aobj, struct rx_connection *aconn,
222                   struct rx_packet *apacket)
223 {
224     struct rxkad_cprivate *tcp;
225     char *tp;
226     int v2;                     /* whether server is old style or v2 */
227     afs_int32 challengeID;
228     rxkad_level level;
229     char *response;
230     int responseSize, missing;
231     struct rxkad_v2ChallengeResponse r_v2;
232     struct rxkad_oldChallengeResponse r_old;
233
234     tcp = (struct rxkad_cprivate *)aobj->privateData;
235
236     if (!(tcp->type & rxkad_client))
237         return RXKADINCONSISTENCY;
238
239     v2 = (rx_Contiguous(apacket) > sizeof(struct rxkad_oldChallenge));
240     tp = rx_DataOf(apacket);
241
242     if (v2) {                   /* v2 challenge */
243         struct rxkad_v2Challenge *c_v2;
244         if (rx_GetDataSize(apacket) < sizeof(struct rxkad_v2Challenge))
245             return RXKADPACKETSHORT;
246         c_v2 = (struct rxkad_v2Challenge *)tp;
247         challengeID = ntohl(c_v2->challengeID);
248         level = ntohl(c_v2->level);
249     } else {                    /* old format challenge */
250         struct rxkad_oldChallenge *c_old;
251         if (rx_GetDataSize(apacket) < sizeof(struct rxkad_oldChallenge))
252             return RXKADPACKETSHORT;
253         c_old = (struct rxkad_oldChallenge *)tp;
254         challengeID = ntohl(c_old->challengeID);
255         level = ntohl(c_old->level);
256     }
257
258     if (level > tcp->level)
259         return RXKADLEVELFAIL;
260     INC_RXKAD_STATS(challenges[rxkad_LevelIndex(tcp->level)]);
261     if (v2) {
262         int i;
263         afs_uint32 xor[2];
264         memset((void *)&r_v2, 0, sizeof(r_v2));
265         r_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
266         r_v2.spare = 0;
267         (void)rxkad_SetupEndpoint(aconn, &r_v2.encrypted.endpoint);
268         (void)rxi_GetCallNumberVector(aconn, r_v2.encrypted.callNumbers);
269         for (i = 0; i < RX_MAXCALLS; i++) {
270             if (r_v2.encrypted.callNumbers[i] < 0)
271                 return RXKADINCONSISTENCY;
272             r_v2.encrypted.callNumbers[i] =
273                 htonl(r_v2.encrypted.callNumbers[i]);
274         }
275         r_v2.encrypted.incChallengeID = htonl(challengeID + 1);
276         r_v2.encrypted.level = htonl((afs_int32) tcp->level);
277         r_v2.kvno = htonl(tcp->kvno);
278         r_v2.ticketLen = htonl(tcp->ticketLen);
279         r_v2.encrypted.endpoint.cksum = rxkad_CksumChallengeResponse(&r_v2);
280         memcpy((void *)xor, (void *)tcp->ivec, 2 * sizeof(afs_int32));
281         fc_cbc_encrypt(&r_v2.encrypted, &r_v2.encrypted,
282                        sizeof(r_v2.encrypted), tcp->keysched, xor, ENCRYPT);
283         response = (char *)&r_v2;
284         responseSize = sizeof(r_v2);
285     } else {
286         memset((void *)&r_old, 0, sizeof(r_old));
287         r_old.encrypted.incChallengeID = htonl(challengeID + 1);
288         r_old.encrypted.level = htonl((afs_int32) tcp->level);
289         r_old.kvno = htonl(tcp->kvno);
290         r_old.ticketLen = htonl(tcp->ticketLen);
291         fc_ecb_encrypt(&r_old.encrypted, &r_old.encrypted, tcp->keysched,
292                        ENCRYPT);
293         response = (char *)&r_old;
294         responseSize = sizeof(r_old);
295     }
296
297     if (RX_MAX_PACKET_DATA_SIZE < responseSize + tcp->ticketLen)
298         return RXKADPACKETSHORT;        /* not enough space */
299
300     rx_computelen(apacket, missing);
301     missing = responseSize + tcp->ticketLen - missing;
302     if (missing > 0)
303         if (rxi_AllocDataBuf(apacket, missing, RX_PACKET_CLASS_SEND) > 0)
304             return RXKADPACKETSHORT;    /* not enough space */
305
306     /* copy response and ticket into packet */
307     rx_packetwrite(apacket, 0, responseSize, response);
308     rx_packetwrite(apacket, responseSize, tcp->ticketLen, tcp->ticket);
309
310     rx_SetDataSize(apacket, responseSize + tcp->ticketLen);
311     return 0;
312 }
313
314 void
315 rxkad_ResetState(void)
316 {
317     LOCK_CUID;
318     Cuid[0] = 0;
319     rxkad_EpochWasSet = 0;
320     UNLOCK_CUID;
321 }