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