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