r23779: Change from v2 or later to v3 or later.
[kai/samba-autobuild/.git] / source / libsmb / credentials.c
1 /* 
2    Unix SMB/CIFS implementation.
3    code to manipulate domain credentials
4    Copyright (C) Andrew Tridgell 1997-1998
5    Largely rewritten by Jeremy Allison 2005.
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 /****************************************************************************
25  Represent a credential as a string.
26 ****************************************************************************/
27
28 char *credstr(const unsigned char *cred)
29 {
30         static fstring buf;
31         slprintf(buf, sizeof(buf) - 1, "%02X%02X%02X%02X%02X%02X%02X%02X",
32                 cred[0], cred[1], cred[2], cred[3], 
33                 cred[4], cred[5], cred[6], cred[7]);
34         return buf;
35 }
36
37 /****************************************************************************
38  Setup the session key and the client and server creds in dc.
39  ADS-style 128 bit session keys.
40  Used by both client and server creds setup.
41 ****************************************************************************/
42
43 static void creds_init_128(struct dcinfo *dc,
44                                 const DOM_CHAL *clnt_chal_in,
45                                 const DOM_CHAL *srv_chal_in,
46                                 const unsigned char mach_pw[16])
47 {
48         unsigned char zero[4], tmp[16];
49         HMACMD5Context ctx;
50         struct MD5Context md5;
51
52         /* Just in case this isn't already there */
53         memcpy(dc->mach_pw, mach_pw, 16);
54
55         ZERO_STRUCT(dc->sess_key);
56
57         memset(zero, 0, sizeof(zero));
58
59         hmac_md5_init_rfc2104(mach_pw, 16, &ctx);
60         MD5Init(&md5);
61         MD5Update(&md5, zero, sizeof(zero));
62         MD5Update(&md5, clnt_chal_in->data, 8);
63         MD5Update(&md5, srv_chal_in->data, 8);
64         MD5Final(tmp, &md5);
65         hmac_md5_update(tmp, sizeof(tmp), &ctx);
66         hmac_md5_final(dc->sess_key, &ctx);
67
68         /* debug output */
69         DEBUG(5,("creds_init_128\n"));
70         DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
71         DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
72         dump_data_pw("\tsession_key ", (const unsigned char *)dc->sess_key, 16);
73
74         /* Generate the next client and server creds. */
75         
76         des_crypt112(dc->clnt_chal.data,                /* output */
77                         clnt_chal_in->data,             /* input */
78                         dc->sess_key,                   /* input */
79                         1);
80
81         des_crypt112(dc->srv_chal.data,                 /* output */
82                         srv_chal_in->data,              /* input */
83                         dc->sess_key,                   /* input */
84                         1);
85
86         /* Seed is the client chal. */
87         memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
88 }
89
90 /****************************************************************************
91  Setup the session key and the client and server creds in dc.
92  Used by both client and server creds setup.
93 ****************************************************************************/
94
95 static void creds_init_64(struct dcinfo *dc,
96                         const DOM_CHAL *clnt_chal_in,
97                         const DOM_CHAL *srv_chal_in,
98                         const unsigned char mach_pw[16])
99 {
100         uint32 sum[2];
101         unsigned char sum2[8];
102
103         /* Just in case this isn't already there */
104         if (dc->mach_pw != mach_pw) {
105                 memcpy(dc->mach_pw, mach_pw, 16);
106         }
107
108         sum[0] = IVAL(clnt_chal_in->data, 0) + IVAL(srv_chal_in->data, 0);
109         sum[1] = IVAL(clnt_chal_in->data, 4) + IVAL(srv_chal_in->data, 4);
110
111         SIVAL(sum2,0,sum[0]);
112         SIVAL(sum2,4,sum[1]);
113
114         ZERO_STRUCT(dc->sess_key);
115
116         des_crypt128(dc->sess_key, sum2, dc->mach_pw);
117
118         /* debug output */
119         DEBUG(5,("creds_init_64\n"));
120         DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
121         DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
122         DEBUG(5,("\tclnt+srv : %s\n", credstr(sum2)));
123         DEBUG(5,("\tsess_key_out : %s\n", credstr(dc->sess_key)));
124
125         /* Generate the next client and server creds. */
126         
127         des_crypt112(dc->clnt_chal.data,                /* output */
128                         clnt_chal_in->data,             /* input */
129                         dc->sess_key,                   /* input */
130                         1);
131
132         des_crypt112(dc->srv_chal.data,                 /* output */
133                         srv_chal_in->data,              /* input */
134                         dc->sess_key,                   /* input */
135                         1);
136
137         /* Seed is the client chal. */
138         memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
139 }
140
141 /****************************************************************************
142  Utility function to step credential chain one forward.
143  Deliberately doesn't update the seed. See reseed comment below.
144 ****************************************************************************/
145
146 static void creds_step(struct dcinfo *dc)
147 {
148         DOM_CHAL time_chal;
149
150         DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc->sequence ));
151
152         DEBUG(5,("\tseed:        %s\n", credstr(dc->seed_chal.data) ));
153
154         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence);
155         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
156                                                                                                    
157         DEBUG(5,("\tseed+seq   %s\n", credstr(time_chal.data) ));
158
159         des_crypt112(dc->clnt_chal.data, time_chal.data, dc->sess_key, 1);
160
161         DEBUG(5,("\tCLIENT      %s\n", credstr(dc->clnt_chal.data) ));
162
163         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
164         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
165
166         DEBUG(5,("\tseed+seq+1   %s\n", credstr(time_chal.data) ));
167
168         des_crypt112(dc->srv_chal.data, time_chal.data, dc->sess_key, 1);
169
170         DEBUG(5,("\tSERVER      %s\n", credstr(dc->srv_chal.data) ));
171 }
172
173 /****************************************************************************
174  Create a server credential struct.
175 ****************************************************************************/
176
177 void creds_server_init(uint32 neg_flags,
178                         struct dcinfo *dc,
179                         DOM_CHAL *clnt_chal,
180                         DOM_CHAL *srv_chal,
181                         const unsigned char mach_pw[16],
182                         DOM_CHAL *init_chal_out)
183 {
184         DEBUG(10,("creds_server_init: neg_flags : %x\n", (unsigned int)neg_flags));
185         DEBUG(10,("creds_server_init: client chal : %s\n", credstr(clnt_chal->data) ));
186         DEBUG(10,("creds_server_init: server chal : %s\n", credstr(srv_chal->data) ));
187         dump_data_pw("creds_server_init: machine pass", mach_pw, 16);
188
189         /* Generate the session key and the next client and server creds. */
190         if (neg_flags & NETLOGON_NEG_128BIT) {
191                 creds_init_128(dc,
192                         clnt_chal,
193                         srv_chal,
194                         mach_pw);
195         } else {
196                 creds_init_64(dc,
197                         clnt_chal,
198                         srv_chal,
199                         mach_pw);
200         }
201
202         dump_data_pw("creds_server_init: session key", dc->sess_key, 16);
203
204         DEBUG(10,("creds_server_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
205         DEBUG(10,("creds_server_init: server : %s\n", credstr(dc->srv_chal.data) ));
206         DEBUG(10,("creds_server_init: seed : %s\n", credstr(dc->seed_chal.data) ));
207
208         memcpy(init_chal_out->data, dc->srv_chal.data, 8);
209 }
210
211 /****************************************************************************
212  Check a credential sent by the client.
213 ****************************************************************************/
214
215 BOOL creds_server_check(const struct dcinfo *dc, const DOM_CHAL *rcv_cli_chal_in)
216 {
217         if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
218                 DEBUG(5,("creds_server_check: challenge : %s\n", credstr(rcv_cli_chal_in->data)));
219                 DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
220                 DEBUG(2,("creds_server_check: credentials check failed.\n"));
221                 return False;
222         }
223         DEBUG(10,("creds_server_check: credentials check OK.\n"));
224         return True;
225 }
226
227 /****************************************************************************
228  Replace current seed chal. Internal function - due to split server step below.
229 ****************************************************************************/
230
231 static void creds_reseed(struct dcinfo *dc)
232 {
233         DOM_CHAL time_chal;
234
235         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
236         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
237
238         dc->seed_chal = time_chal;
239
240         DEBUG(5,("cred_reseed: seed %s\n", credstr(dc->seed_chal.data) ));
241 }
242
243 /****************************************************************************
244  Step the server credential chain one forward. 
245 ****************************************************************************/
246
247 BOOL creds_server_step(struct dcinfo *dc, const DOM_CRED *received_cred, DOM_CRED *cred_out)
248 {
249         BOOL ret;
250         struct dcinfo tmp_dc = *dc;
251
252         /* Do all operations on a temporary copy of the dc,
253            which we throw away if the checks fail. */
254
255         tmp_dc.sequence = received_cred->timestamp.time;
256
257         creds_step(&tmp_dc);
258
259         /* Create the outgoing credentials */
260         cred_out->timestamp.time = tmp_dc.sequence + 1;
261         cred_out->challenge = tmp_dc.srv_chal;
262
263         creds_reseed(&tmp_dc);
264
265         ret = creds_server_check(&tmp_dc, &received_cred->challenge);
266         if (!ret) {
267                 return False;
268         }
269
270         /* creds step succeeded - replace the current creds. */
271         *dc = tmp_dc;
272         return True;
273 }
274
275 /****************************************************************************
276  Create a client credential struct.
277 ****************************************************************************/
278
279 void creds_client_init(uint32 neg_flags,
280                         struct dcinfo *dc,
281                         DOM_CHAL *clnt_chal,
282                         DOM_CHAL *srv_chal,
283                         const unsigned char mach_pw[16],
284                         DOM_CHAL *init_chal_out)
285 {
286         dc->sequence = time(NULL);
287
288         DEBUG(10,("creds_client_init: neg_flags : %x\n", (unsigned int)neg_flags));
289         DEBUG(10,("creds_client_init: client chal : %s\n", credstr(clnt_chal->data) ));
290         DEBUG(10,("creds_client_init: server chal : %s\n", credstr(srv_chal->data) ));
291         dump_data_pw("creds_client_init: machine pass", (const unsigned char *)mach_pw, 16);
292
293         /* Generate the session key and the next client and server creds. */
294         if (neg_flags & NETLOGON_NEG_128BIT) {
295                 creds_init_128(dc,
296                                 clnt_chal,
297                                 srv_chal,
298                                 mach_pw);
299         } else {
300                 creds_init_64(dc,
301                         clnt_chal,
302                         srv_chal,
303                         mach_pw);
304         }
305
306         dump_data_pw("creds_client_init: session key", dc->sess_key, 16);
307
308         DEBUG(10,("creds_client_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
309         DEBUG(10,("creds_client_init: server : %s\n", credstr(dc->srv_chal.data) ));
310         DEBUG(10,("creds_client_init: seed : %s\n", credstr(dc->seed_chal.data) ));
311
312         memcpy(init_chal_out->data, dc->clnt_chal.data, 8);
313 }
314
315 /****************************************************************************
316  Check a credential returned by the server.
317 ****************************************************************************/
318
319 BOOL creds_client_check(const struct dcinfo *dc, const DOM_CHAL *rcv_srv_chal_in)
320 {
321         if (memcmp(dc->srv_chal.data, rcv_srv_chal_in->data, 8)) {
322                 DEBUG(5,("creds_client_check: challenge : %s\n", credstr(rcv_srv_chal_in->data)));
323                 DEBUG(5,("calculated: %s\n", credstr(dc->srv_chal.data)));
324                 DEBUG(0,("creds_client_check: credentials check failed.\n"));
325                 return False;
326         }
327         DEBUG(10,("creds_client_check: credentials check OK.\n"));
328         return True;
329 }
330
331 /****************************************************************************
332   Step the client credentials to the next element in the chain, updating the
333   current client and server credentials and the seed
334   produce the next authenticator in the sequence ready to send to
335   the server
336 ****************************************************************************/
337
338 void creds_client_step(struct dcinfo *dc, DOM_CRED *next_cred_out)
339 {
340         dc->sequence += 2;
341         creds_step(dc);
342         creds_reseed(dc);
343
344         next_cred_out->challenge = dc->clnt_chal;
345         next_cred_out->timestamp.time = dc->sequence;
346 }