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.
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 2 of the License, or
10 (at your option) any later version.
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.
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.
24 /****************************************************************************
25 Represent a credential as a string.
26 ****************************************************************************/
28 char *credstr(const unsigned char *cred
)
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]);
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 ****************************************************************************/
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])
48 unsigned char zero
[4], tmp
[16];
50 struct MD5Context md5
;
52 /* Just in case this isn't already there */
53 memcpy(dc
->mach_pw
, mach_pw
, 16);
55 ZERO_STRUCT(dc
->sess_key
);
57 memset(zero
, 0, sizeof(zero
));
59 hmac_md5_init_rfc2104(mach_pw
, 16, &ctx
);
61 MD5Update(&md5
, zero
, sizeof(zero
));
62 MD5Update(&md5
, clnt_chal_in
->data
, 8);
63 MD5Update(&md5
, srv_chal_in
->data
, 8);
65 hmac_md5_update(tmp
, sizeof(tmp
), &ctx
);
66 hmac_md5_final(dc
->sess_key
, &ctx
);
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);
74 /* Generate the next client and server creds. */
76 des_crypt112(dc
->clnt_chal
.data
, /* output */
77 clnt_chal_in
->data
, /* input */
78 dc
->sess_key
, /* input */
81 des_crypt112(dc
->srv_chal
.data
, /* output */
82 srv_chal_in
->data
, /* input */
83 dc
->sess_key
, /* input */
86 /* Seed is the client chal. */
87 memcpy(dc
->seed_chal
.data
, dc
->clnt_chal
.data
, 8);
90 /****************************************************************************
91 Setup the session key and the client and server creds in dc.
92 Used by both client and server creds setup.
93 ****************************************************************************/
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])
101 unsigned char sum2
[8];
103 /* Just in case this isn't already there */
104 memcpy(dc
->mach_pw
, mach_pw
, 16);
106 sum
[0] = IVAL(clnt_chal_in
->data
, 0) + IVAL(srv_chal_in
->data
, 0);
107 sum
[1] = IVAL(clnt_chal_in
->data
, 4) + IVAL(srv_chal_in
->data
, 4);
109 SIVAL(sum2
,0,sum
[0]);
110 SIVAL(sum2
,4,sum
[1]);
112 ZERO_STRUCT(dc
->sess_key
);
114 des_crypt128(dc
->sess_key
, sum2
, dc
->mach_pw
);
117 DEBUG(5,("creds_init_64\n"));
118 DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in
->data
)));
119 DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in
->data
)));
120 DEBUG(5,("\tclnt+srv : %s\n", credstr(sum2
)));
121 DEBUG(5,("\tsess_key_out : %s\n", credstr(dc
->sess_key
)));
123 /* Generate the next client and server creds. */
125 des_crypt112(dc
->clnt_chal
.data
, /* output */
126 clnt_chal_in
->data
, /* input */
127 dc
->sess_key
, /* input */
130 des_crypt112(dc
->srv_chal
.data
, /* output */
131 srv_chal_in
->data
, /* input */
132 dc
->sess_key
, /* input */
135 /* Seed is the client chal. */
136 memcpy(dc
->seed_chal
.data
, dc
->clnt_chal
.data
, 8);
139 /****************************************************************************
140 Utility function to step credential chain one forward.
141 Deliberately doesn't update the seed. See reseed comment below.
142 ****************************************************************************/
144 static void creds_step(struct dcinfo
*dc
)
148 DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc
->sequence
));
150 DEBUG(5,("\tseed: %s\n", credstr(dc
->seed_chal
.data
) ));
152 SIVAL(time_chal
.data
, 0, IVAL(dc
->seed_chal
.data
, 0) + dc
->sequence
);
153 SIVAL(time_chal
.data
, 4, IVAL(dc
->seed_chal
.data
, 4));
155 DEBUG(5,("\tseed+seq %s\n", credstr(time_chal
.data
) ));
157 des_crypt112(dc
->clnt_chal
.data
, time_chal
.data
, dc
->sess_key
, 1);
159 DEBUG(5,("\tCLIENT %s\n", credstr(dc
->clnt_chal
.data
) ));
161 SIVAL(time_chal
.data
, 0, IVAL(dc
->seed_chal
.data
, 0) + dc
->sequence
+ 1);
162 SIVAL(time_chal
.data
, 4, IVAL(dc
->seed_chal
.data
, 4));
164 DEBUG(5,("\tseed+seq+1 %s\n", credstr(time_chal
.data
) ));
166 des_crypt112(dc
->srv_chal
.data
, time_chal
.data
, dc
->sess_key
, 1);
168 DEBUG(5,("\tSERVER %s\n", credstr(dc
->srv_chal
.data
) ));
171 /****************************************************************************
172 Create a server credential struct.
173 ****************************************************************************/
175 void creds_server_init(uint32 neg_flags
,
179 const unsigned char mach_pw
[16],
180 DOM_CHAL
*init_chal_out
)
182 DEBUG(10,("creds_server_init: neg_flags : %x\n", (unsigned int)neg_flags
));
183 DEBUG(10,("creds_server_init: client chal : %s\n", credstr(clnt_chal
->data
) ));
184 DEBUG(10,("creds_server_init: server chal : %s\n", credstr(srv_chal
->data
) ));
185 dump_data_pw("creds_server_init: machine pass", mach_pw
, 16);
187 /* Generate the session key and the next client and server creds. */
188 if (neg_flags
& NETLOGON_NEG_128BIT
) {
200 dump_data_pw("creds_server_init: session key", dc
->sess_key
, 16);
202 DEBUG(10,("creds_server_init: clnt : %s\n", credstr(dc
->clnt_chal
.data
) ));
203 DEBUG(10,("creds_server_init: server : %s\n", credstr(dc
->srv_chal
.data
) ));
204 DEBUG(10,("creds_server_init: seed : %s\n", credstr(dc
->seed_chal
.data
) ));
206 memcpy(init_chal_out
->data
, dc
->srv_chal
.data
, 8);
209 /****************************************************************************
210 Check a credential sent by the client.
211 ****************************************************************************/
213 BOOL
creds_server_check(const struct dcinfo
*dc
, const DOM_CHAL
*rcv_cli_chal_in
)
215 if (memcmp(dc
->clnt_chal
.data
, rcv_cli_chal_in
->data
, 8)) {
216 DEBUG(5,("creds_server_check: challenge : %s\n", credstr(rcv_cli_chal_in
->data
)));
217 DEBUG(5,("calculated: %s\n", credstr(dc
->clnt_chal
.data
)));
218 DEBUG(2,("creds_server_check: credentials check failed.\n"));
221 DEBUG(10,("creds_server_check: credentials check OK.\n"));
225 /****************************************************************************
226 Replace current seed chal. Internal function - due to split server step below.
227 ****************************************************************************/
229 static void creds_reseed(struct dcinfo
*dc
)
233 SIVAL(time_chal
.data
, 0, IVAL(dc
->seed_chal
.data
, 0) + dc
->sequence
+ 1);
234 SIVAL(time_chal
.data
, 4, IVAL(dc
->seed_chal
.data
, 4));
236 dc
->seed_chal
= time_chal
;
238 DEBUG(5,("cred_reseed: seed %s\n", credstr(dc
->seed_chal
.data
) ));
241 /****************************************************************************
242 Step the server credential chain one forward.
243 ****************************************************************************/
245 BOOL
creds_server_step(struct dcinfo
*dc
, const DOM_CRED
*received_cred
, DOM_CRED
*cred_out
)
248 struct dcinfo tmp_dc
= *dc
;
250 /* Do all operations on a temporary copy of the dc,
251 which we throw away if the checks fail. */
253 tmp_dc
.sequence
= received_cred
->timestamp
.time
;
257 /* Create the outgoing credentials */
258 cred_out
->timestamp
.time
= tmp_dc
.sequence
+ 1;
259 cred_out
->challenge
= tmp_dc
.srv_chal
;
261 creds_reseed(&tmp_dc
);
263 ret
= creds_server_check(&tmp_dc
, &received_cred
->challenge
);
268 /* creds step succeeded - replace the current creds. */
273 /****************************************************************************
274 Create a client credential struct.
275 ****************************************************************************/
277 void creds_client_init(uint32 neg_flags
,
281 const unsigned char mach_pw
[16],
282 DOM_CHAL
*init_chal_out
)
284 dc
->sequence
= time(NULL
);
286 DEBUG(10,("creds_client_init: neg_flags : %x\n", (unsigned int)neg_flags
));
287 DEBUG(10,("creds_client_init: client chal : %s\n", credstr(clnt_chal
->data
) ));
288 DEBUG(10,("creds_client_init: server chal : %s\n", credstr(srv_chal
->data
) ));
289 dump_data_pw("creds_client_init: machine pass", (const unsigned char *)mach_pw
, 16);
291 /* Generate the session key and the next client and server creds. */
292 if (neg_flags
& NETLOGON_NEG_128BIT
) {
304 dump_data_pw("creds_client_init: session key", dc
->sess_key
, 16);
306 DEBUG(10,("creds_client_init: clnt : %s\n", credstr(dc
->clnt_chal
.data
) ));
307 DEBUG(10,("creds_client_init: server : %s\n", credstr(dc
->srv_chal
.data
) ));
308 DEBUG(10,("creds_client_init: seed : %s\n", credstr(dc
->seed_chal
.data
) ));
310 memcpy(init_chal_out
->data
, dc
->clnt_chal
.data
, 8);
313 /****************************************************************************
314 Check a credential returned by the server.
315 ****************************************************************************/
317 BOOL
creds_client_check(const struct dcinfo
*dc
, const DOM_CHAL
*rcv_srv_chal_in
)
319 if (memcmp(dc
->srv_chal
.data
, rcv_srv_chal_in
->data
, 8)) {
320 DEBUG(5,("creds_client_check: challenge : %s\n", credstr(rcv_srv_chal_in
->data
)));
321 DEBUG(5,("calculated: %s\n", credstr(dc
->srv_chal
.data
)));
322 DEBUG(0,("creds_client_check: credentials check failed.\n"));
325 DEBUG(10,("creds_client_check: credentials check OK.\n"));
329 /****************************************************************************
330 Step the client credentials to the next element in the chain, updating the
331 current client and server credentials and the seed
332 produce the next authenticator in the sequence ready to send to
334 ****************************************************************************/
336 void creds_client_step(struct dcinfo
*dc
, DOM_CRED
*next_cred_out
)
342 next_cred_out
->challenge
= dc
->clnt_chal
;
343 next_cred_out
->timestamp
.time
= dc
->sequence
;