xref: /illumos-gate/usr/src/lib/libsmbfs/smb/ntlmssp.c (revision 40c0e231)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
25  */
26 
27 /*
28  * NT Lan Manager Security Support Provider (NTLMSSP)
29  *
30  * Based on information from the "Davenport NTLM" page:
31  * http://davenport.sourceforge.net/ntlm.html
32  */
33 
34 
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stddef.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <strings.h>
41 #include <netdb.h>
42 #include <libintl.h>
43 #include <xti.h>
44 #include <assert.h>
45 
46 #include <sys/types.h>
47 #include <sys/time.h>
48 #include <sys/byteorder.h>
49 #include <sys/socket.h>
50 #include <sys/fcntl.h>
51 
52 #include <netinet/in.h>
53 #include <netinet/tcp.h>
54 #include <arpa/inet.h>
55 
56 #include <netsmb/smb.h>
57 #include <netsmb/smb_lib.h>
58 #include <netsmb/mchain.h>
59 
60 #include "private.h"
61 #include "charsets.h"
62 #include "smb_crypt.h"
63 #include "spnego.h"
64 #include "derparse.h"
65 #include "ssp.h"
66 #include "ntlm.h"
67 #include "ntlmssp.h"
68 
69 /* A shorter alias for a crazy long name from [MS-NLMP] */
70 #define	NTLMSSP_NEGOTIATE_ESS \
71 	NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
72 
73 typedef struct ntlmssp_state {
74 	uint32_t ss_flags;
75 	char *ss_target_name;	/* Primary domain or server name */
76 	struct mbuf *ss_target_info;
77 	uchar_t	ss_ssnkey[NTLM_HASH_SZ];
78 	uchar_t	ss_kxkey[NTLM_HASH_SZ];
79 } ntlmssp_state_t;
80 
81 /*
82  * So called "security buffer".
83  * A lot like an RPC string.
84  */
85 struct sec_buf {
86 	uint16_t sb_length;
87 	uint16_t sb_maxlen;
88 	uint32_t sb_offset;
89 };
90 #define	ID_SZ 8
91 static const char ntlmssp_id[ID_SZ] = "NTLMSSP";
92 
93 static int
94 ntlm_rand_ssn_key(ntlmssp_state_t *ssp_st, struct mbdata *ek_mbp);
95 
96 /*
97  * Get a "security buffer" (header part)
98  */
99 static int
md_get_sb_hdr(struct mbdata * mbp,struct sec_buf * sb)100 md_get_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
101 {
102 	int err;
103 
104 	(void) md_get_uint16le(mbp, &sb->sb_length);
105 	(void) md_get_uint16le(mbp, &sb->sb_maxlen);
106 	err = md_get_uint32le(mbp, &sb->sb_offset);
107 
108 	return (err);
109 }
110 
111 /*
112  * Get a "security buffer" (data part), where
113  * the data is delivered as an mbuf.
114  */
115 static int
md_get_sb_data(struct mbdata * mbp,struct sec_buf * sb,struct mbuf ** mp)116 md_get_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf **mp)
117 {
118 	struct mbdata tmp_mb;
119 	int err;
120 
121 	/*
122 	 * Setup tmp_mb to point to the start of the header.
123 	 * This is a dup ref - do NOT free it.
124 	 */
125 	mb_initm(&tmp_mb, mbp->mb_top);
126 
127 	/* Skip data up to the offset. */
128 	err = md_get_mem(&tmp_mb, NULL, sb->sb_offset, MB_MSYSTEM);
129 	if (err)
130 		return (err);
131 
132 	/* Get the data (as an mbuf). */
133 	err = md_get_mbuf(&tmp_mb, sb->sb_maxlen, mp);
134 
135 	return (err);
136 }
137 
138 /*
139  * Put a "security buffer" (header part)
140  */
141 static int
mb_put_sb_hdr(struct mbdata * mbp,struct sec_buf * sb)142 mb_put_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
143 {
144 	int err;
145 
146 	(void) mb_put_uint16le(mbp, sb->sb_length);
147 	(void) mb_put_uint16le(mbp, sb->sb_maxlen);
148 	err = mb_put_uint32le(mbp, sb->sb_offset);
149 
150 	return (err);
151 }
152 
153 /*
154  * Put a "security buffer" (data part), where
155  * the data is an mbuf.  Note: consumes m.
156  */
157 static int
mb_put_sb_data(struct mbdata * mbp,struct sec_buf * sb,struct mbuf * m)158 mb_put_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf *m)
159 {
160 	int cnt0;
161 	int err = 0;
162 
163 	sb->sb_offset = cnt0 = mbp->mb_count;
164 	if (m != NULL)
165 		err = mb_put_mbuf(mbp, m);
166 	sb->sb_maxlen = sb->sb_length = mbp->mb_count - cnt0;
167 
168 	return (err);
169 }
170 
171 /*
172  * Put a "security buffer" (data part), where
173  * the data is a string (OEM or unicode).
174  *
175  * The string is NOT null terminated.
176  */
177 static int
mb_put_sb_string(struct mbdata * mbp,struct sec_buf * sb,const char * str,int unicode)178 mb_put_sb_string(struct mbdata *mbp, struct sec_buf *sb,
179 	const char *str, int unicode)
180 {
181 	int err, trim;
182 	struct mbdata tmp_mb;
183 
184 	bzero(&tmp_mb, sizeof (tmp_mb));
185 
186 	if (str != NULL && *str != '\0') {
187 		/*
188 		 * Put the string into a temp. mbuf,
189 		 * then chop off the null terminator
190 		 * before appending to caller's mbp.
191 		 */
192 		err = mb_init(&tmp_mb);
193 		if (err)
194 			return (err);
195 		err = mb_put_string(&tmp_mb, str, unicode);
196 		if (err)
197 			return (err);
198 
199 		trim = (unicode) ? 2 : 1;
200 		if (tmp_mb.mb_cur->m_len < trim)
201 			trim = 0;
202 		tmp_mb.mb_cur->m_len -= trim;
203 	}
204 
205 	err = mb_put_sb_data(mbp, sb, tmp_mb.mb_top);
206 	/*
207 	 * Note: tmp_mb.mb_top (if any) is consumed,
208 	 * so do NOT free it (no mb_done)
209 	 */
210 	return (err);
211 }
212 
213 /*
214  * Build a Type 1 message
215  *
216  * This message has a header section containing offsets to
217  * data later in the message.  We use the common trick of
218  * building it in two parts and then concatenatening.
219  */
220 int
ntlmssp_put_type1(struct ssp_ctx * sp,struct mbdata * out_mb)221 ntlmssp_put_type1(struct ssp_ctx *sp, struct mbdata *out_mb)
222 {
223 	struct type1hdr {
224 		char h_id[ID_SZ];
225 		uint32_t h_type;
226 		uint32_t h_flags;
227 		struct sec_buf h_cldom;
228 		struct sec_buf h_wksta;
229 	} hdr;
230 	struct mbdata mb2;	/* 2nd part */
231 	int err;
232 	struct smb_ctx *ctx = sp->smb_ctx;
233 	ntlmssp_state_t *ssp_st = sp->sp_private;
234 
235 	if ((err = mb_init(&mb2)) != 0)
236 		return (err);
237 	mb2.mb_count = sizeof (hdr);
238 
239 	/*
240 	 * The initial negotiation flags represent the union of all
241 	 * options we support.  The server selects from these.
242 	 * See: [MS-NLMP 2.2.2.5 NEGOTIATE]
243 	 */
244 	ssp_st->ss_flags =
245 	    NTLMSSP_NEGOTIATE_UNICODE |
246 	    NTLMSSP_NEGOTIATE_OEM |
247 	    NTLMSSP_REQUEST_TARGET |
248 	    NTLMSSP_NEGOTIATE_SIGN |
249 	    NTLMSSP_NEGOTIATE_SEAL |
250 	    /* NTLMSSP_NEGOTIATE_LM_KEY (never) */
251 	    NTLMSSP_NEGOTIATE_NTLM |
252 	    NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
253 	    NTLMSSP_NEGOTIATE_ESS |
254 	    NTLMSSP_NEGOTIATE_128 |
255 	    NTLMSSP_NEGOTIATE_KEY_EXCH |
256 	    NTLMSSP_NEGOTIATE_56;
257 
258 	if ((ctx->ct_vopt & SMBVOPT_SIGNING_ENABLED) == 0)
259 		ssp_st->ss_flags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
260 
261 	bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
262 	hdr.h_type = NTLMSSP_MSGTYPE_NEGOTIATE;
263 	hdr.h_flags = ssp_st->ss_flags;
264 
265 	/*
266 	 * We could put the client domain, client name strings
267 	 * here, (always in OEM format, upper-case), and set
268 	 * NTLMSSP_NEGOTIATE_OEM_..._SUPPLIED, but Windows
269 	 * leaves these NULL so let's do the same.
270 	 */
271 	(void) mb_put_sb_string(&mb2, &hdr.h_cldom, NULL, 0);
272 	(void) mb_put_sb_string(&mb2, &hdr.h_wksta, NULL, 0);
273 
274 	/*
275 	 * Marshal the header (in LE order)
276 	 * then concatenate the 2nd part.
277 	 */
278 	(void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
279 	(void) mb_put_uint32le(out_mb, hdr.h_type);
280 	(void) mb_put_uint32le(out_mb, hdr.h_flags);
281 	(void) mb_put_sb_hdr(out_mb, &hdr.h_cldom);
282 	(void) mb_put_sb_hdr(out_mb, &hdr.h_wksta);
283 
284 	err = mb_put_mbuf(out_mb, mb2.mb_top);
285 
286 	return (err);
287 }
288 
289 /*
290  * Parse a Type 2 message
291  */
292 int
ntlmssp_get_type2(struct ssp_ctx * sp,struct mbdata * in_mb)293 ntlmssp_get_type2(struct ssp_ctx *sp, struct mbdata *in_mb)
294 {
295 	struct type2hdr {
296 		char h_id[ID_SZ];
297 		uint32_t h_type;
298 		struct sec_buf h_target_name;
299 		uint32_t h_flags;
300 		uint8_t h_challenge[8];
301 		uint32_t h_context[2];		/* optional */
302 		struct sec_buf h_target_info;	/* optional */
303 	} hdr;
304 	struct mbdata top_mb, tmp_mb;
305 	struct mbuf *m;
306 	int err, uc;
307 	int min_hdr_sz = offsetof(struct type2hdr, h_context);
308 	struct smb_ctx *ctx = sp->smb_ctx;
309 	ntlmssp_state_t *ssp_st = sp->sp_private;
310 	char *buf = NULL;
311 
312 	if (m_totlen(in_mb->mb_top) < min_hdr_sz) {
313 		err = EBADRPC;
314 		goto out;
315 	}
316 
317 	/*
318 	 * Save the mbdata pointers before we consume anything.
319 	 * Careful to NOT free this (would be dup. free)
320 	 * We use this below to find data based on offsets
321 	 * from the start of the header.
322 	 */
323 	top_mb = *in_mb;
324 
325 	/* Parse the fixed size header stuff. */
326 	bzero(&hdr, sizeof (hdr));
327 	(void) md_get_mem(in_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
328 	(void) md_get_uint32le(in_mb, &hdr.h_type);
329 	if (hdr.h_type != NTLMSSP_MSGTYPE_CHALLENGE) {
330 		err = EPROTO;
331 		goto out;
332 	}
333 	(void) md_get_sb_hdr(in_mb, &hdr.h_target_name);
334 	(void) md_get_uint32le(in_mb, &hdr.h_flags);
335 	(void) md_get_mem(in_mb, &hdr.h_challenge, NTLM_CHAL_SZ, MB_MSYSTEM);
336 
337 	/*
338 	 * Save flags, server challenge for later.
339 	 */
340 	ssp_st->ss_flags = hdr.h_flags;
341 	bcopy(&hdr.h_challenge, ctx->ct_srv_chal, NTLM_CHAL_SZ);
342 
343 	/*
344 	 * Turn off flags that might have been given but
345 	 * that we don't want to send with authenticate.
346 	 */
347 	uc = hdr.h_flags & NTLMSSP_NEGOTIATE_UNICODE;
348 	ssp_st->ss_flags &= ~NTLMSSP_NEGOTIATE_VERSION;
349 
350 	/*
351 	 * Now find out if the optional parts are there.
352 	 */
353 	if ((m_totlen(top_mb.mb_top) > sizeof (hdr)) &&
354 	    (hdr.h_target_name.sb_offset >= sizeof (hdr))) {
355 		(void) md_get_uint32le(in_mb, &hdr.h_context[0]);
356 		(void) md_get_uint32le(in_mb, &hdr.h_context[1]);
357 		(void) md_get_sb_hdr(in_mb, &hdr.h_target_info);
358 	}
359 
360 	/*
361 	 * Get the target name string.  (Server name or
362 	 * Primary domain name.)  First get a copy of the
363 	 * data from the offset/length indicated in the
364 	 * security buffer header; then parse the string.
365 	 */
366 	err = md_get_sb_data(&top_mb, &hdr.h_target_name, &m);
367 	if (err)
368 		goto out;
369 	mb_initm(&tmp_mb, m);
370 	err = md_get_string(&tmp_mb, &ssp_st->ss_target_name, uc);
371 	mb_done(&tmp_mb);
372 
373 	/*
374 	 * Get the target info blob, if present.
375 	 */
376 	if (hdr.h_target_info.sb_offset >= sizeof (hdr)) {
377 		err = md_get_sb_data(&top_mb, &hdr.h_target_info,
378 		    &ssp_st->ss_target_info);
379 	}
380 
381 out:
382 	if (buf != NULL)
383 		free(buf);
384 
385 	return (err);
386 }
387 
388 /*
389  * Build a Type 3 message
390  *
391  * This message has a header section containing offsets to
392  * data later in the message.  We use the common trick of
393  * building it in two parts and then concatenatening.
394  */
395 int
ntlmssp_put_type3(struct ssp_ctx * sp,struct mbdata * out_mb)396 ntlmssp_put_type3(struct ssp_ctx *sp, struct mbdata *out_mb)
397 {
398 	struct type3hdr {
399 		char h_id[ID_SZ];
400 		uint32_t h_type;
401 		struct sec_buf h_lm_resp;
402 		struct sec_buf h_nt_resp;
403 		struct sec_buf h_domain;
404 		struct sec_buf h_user;
405 		struct sec_buf h_wksta;
406 		struct sec_buf h_ssn_key;
407 		uint32_t h_flags;
408 		/* Version struct (ommitted) */
409 		uchar_t h_mic[NTLM_HASH_SZ];
410 	} hdr;
411 	struct mbdata lm_mbc;	/* LM response */
412 	struct mbdata nt_mbc;	/* NT response */
413 	struct mbdata ti_mbc;	/* target info */
414 	struct mbdata ek_mbc;	/* encrypted session key */
415 	struct mbdata mb2;	/* payload */
416 	int err, uc;
417 	struct smb_ctx *ctx = sp->smb_ctx;
418 	ntlmssp_state_t *ssp_st = sp->sp_private;
419 	uchar_t *pmic;
420 
421 	bzero(&hdr, sizeof (hdr));
422 	bzero(&lm_mbc, sizeof (lm_mbc));
423 	bzero(&nt_mbc, sizeof (nt_mbc));
424 	bzero(&ti_mbc, sizeof (ti_mbc));
425 	bzero(&ek_mbc, sizeof (ek_mbc));
426 	bzero(&mb2, sizeof (mb2));
427 
428 	/*
429 	 * Fill in the NTLMSSP header, etc.
430 	 */
431 	if ((err = mb_init(&mb2)) != 0)
432 		goto out;
433 	mb2.mb_count = sizeof (hdr);
434 	uc = ssp_st->ss_flags & NTLMSSP_NEGOTIATE_UNICODE;
435 
436 	bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
437 	hdr.h_type = NTLMSSP_MSGTYPE_AUTHENTICATE;
438 	hdr.h_flags = ssp_st->ss_flags;
439 
440 	/*
441 	 * Put the NTLMv2/LMv2 or NTLM/LM (v1) responses,
442 	 * and compute the session key, etc.
443 	 */
444 	if (ctx->ct_authflags & SMB_AT_ANON) {
445 		/*
446 		 * We're setting up a NULL session, meaning
447 		 * the lm_mbc, nt_mbc parts remain empty.
448 		 * Let's add the "anon" flag (hint), and
449 		 * as we have no OWF hashes, we can't use
450 		 * "extended session security" (_ESS).
451 		 * The SessionBaseKey is all zeros, so
452 		 * the KeyExchangeKey is too.  Otherwise
453 		 * this is like NTLMv2/LMv2
454 		 */
455 		ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_NULL_SESSION;
456 		ssp_st->ss_flags &= ~NTLMSSP_NEGOTIATE_ESS;
457 		hdr.h_flags = ssp_st->ss_flags;
458 		err = 0;
459 		/* KeyExchangeKey = SessionBaseKey = (zeros) */
460 		memset(ssp_st->ss_ssnkey, 0, NTLM_HASH_SZ);
461 		memset(ssp_st->ss_kxkey, 0, NTLM_HASH_SZ);
462 	} else if (ctx->ct_authflags & SMB_AT_NTLM2) {
463 		/*
464 		 * Doing NTLMv2/LMv2
465 		 */
466 		err = ntlm_build_target_info(ctx,
467 		    ssp_st->ss_target_info, &ti_mbc);
468 		if (err)
469 			goto out;
470 		err = ntlm_put_v2_responses(ctx, &ti_mbc,
471 		    &lm_mbc, &nt_mbc, ssp_st->ss_ssnkey);
472 		if (err)
473 			goto out;
474 		/* KeyExchangeKey = SessionBaseKey (v2) */
475 		memcpy(ssp_st->ss_kxkey, ssp_st->ss_ssnkey, NTLM_HASH_SZ);
476 	} else if (ssp_st->ss_flags & NTLMSSP_NEGOTIATE_ESS) {
477 		/*
478 		 * Doing NTLM ("v1x") which is NTLM with
479 		 * "Extended Session Security"
480 		 */
481 		err = ntlm_put_v1x_responses(ctx,
482 		    &lm_mbc, &nt_mbc, ssp_st->ss_ssnkey);
483 		if (err)
484 			goto out;
485 		/*
486 		 * "v1x computes the KeyExchangeKey from both the
487 		 * server and client nonce and (v1) SessionBaseKey.
488 		 */
489 		ntlm2_kxkey(ctx, &lm_mbc, ssp_st->ss_ssnkey,
490 		    ssp_st->ss_kxkey);
491 	} else {
492 		/*
493 		 * Doing plain old NTLM (and LM if enabled)
494 		 */
495 		err = ntlm_put_v1_responses(ctx,
496 		    &lm_mbc, &nt_mbc, ssp_st->ss_ssnkey);
497 		if (err)
498 			goto out;
499 		/* KeyExchangeKey = SessionBaseKey (v1) */
500 		memcpy(ssp_st->ss_kxkey, ssp_st->ss_ssnkey, NTLM_HASH_SZ);
501 	}
502 
503 	/*
504 	 * Compute the "ExportedSessionKey" and (possibly) the
505 	 * "EncryptedRandomSesionKey". [MS-NLMP 3.1.5.1.2]
506 	 */
507 	if (ssp_st->ss_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
508 		err = ntlm_rand_ssn_key(ssp_st, &ek_mbc);
509 		if (err)
510 			goto out;
511 	} else {
512 		/* ExportedSessionKey is the KeyExchangeKey */
513 		memcpy(ssp_st->ss_ssnkey, ssp_st->ss_kxkey, NTLM_HASH_SZ);
514 		/* EncryptedRandomSessionKey remains NULL */
515 	}
516 
517 	err = mb_put_sb_data(&mb2, &hdr.h_lm_resp, lm_mbc.mb_top);
518 	lm_mbc.mb_top = NULL; /* consumed */
519 	if (err)
520 		goto out;
521 	err = mb_put_sb_data(&mb2, &hdr.h_nt_resp, nt_mbc.mb_top);
522 	nt_mbc.mb_top = NULL; /* consumed */
523 	if (err)
524 		goto out;
525 
526 	/*
527 	 * Put the "target" (domain), user, workstation
528 	 */
529 	err = mb_put_sb_string(&mb2, &hdr.h_domain, ctx->ct_domain, uc);
530 	if (err)
531 		goto out;
532 	err = mb_put_sb_string(&mb2, &hdr.h_user, ctx->ct_user, uc);
533 	if (err)
534 		goto out;
535 	err = mb_put_sb_string(&mb2, &hdr.h_wksta, ctx->ct_locname, uc);
536 	if (err)
537 		goto out;
538 
539 	/*
540 	 * Put the "Encrypted Random Session Key", if any.
541 	 * (ek_mbc.mb_top may be NULL)
542 	 */
543 	err = mb_put_sb_data(&mb2, &hdr.h_ssn_key, ek_mbc.mb_top);
544 	ek_mbc.mb_top = NULL; /* consumed (if any) */
545 	if (err)
546 		goto out;
547 
548 	/*
549 	 * Marshal the header (in LE order)
550 	 * then concatenate the 2nd part.
551 	 */
552 	(void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
553 	(void) mb_put_uint32le(out_mb, hdr.h_type);
554 
555 	(void) mb_put_sb_hdr(out_mb, &hdr.h_lm_resp);
556 	(void) mb_put_sb_hdr(out_mb, &hdr.h_nt_resp);
557 
558 	(void) mb_put_sb_hdr(out_mb, &hdr.h_domain);
559 	(void) mb_put_sb_hdr(out_mb, &hdr.h_user);
560 	(void) mb_put_sb_hdr(out_mb, &hdr.h_wksta);
561 
562 	(void) mb_put_sb_hdr(out_mb, &hdr.h_ssn_key);
563 	(void) mb_put_uint32le(out_mb, hdr.h_flags);
564 
565 	/* Put zeros for the MIC - filled in later */
566 	pmic = mb_reserve(out_mb, NTLM_HASH_SZ);
567 
568 	/* Put the payload. */
569 	err = mb_put_mbuf(out_mb, mb2.mb_top);
570 	mb2.mb_top = NULL; /* consumed */
571 
572 	/*
573 	 * Compute the MIC and stuff that in...
574 	 * The MIC is apparently optional.
575 	 */
576 	(void) pmic;
577 
578 out:
579 	mb_done(&mb2);
580 	mb_done(&lm_mbc);
581 	mb_done(&nt_mbc);
582 	mb_done(&ti_mbc);
583 	mb_done(&ek_mbc);
584 
585 	return (err);
586 }
587 
588 /*
589  * Helper for ntlmssp_put_type3 when doing key exchange.
590  *
591  * "ExportedSessionKey" is what we give to the "application"
592  * layer, which in here means the MAC key for SMB signing.
593  * With "key exchange", we replace the ExportedSessionKey
594  * with random data and send that (encrypted) to the peer.
595  */
596 static int
ntlm_rand_ssn_key(ntlmssp_state_t * ssp_st,struct mbdata * ek_mbp)597 ntlm_rand_ssn_key(
598 	ntlmssp_state_t *ssp_st,
599 	struct mbdata *ek_mbp)
600 {
601 
602 	uchar_t *encr_ssn_key;
603 	int err;
604 
605 	if ((err = mb_init(ek_mbp)) != 0)
606 		return (err);
607 	encr_ssn_key = mb_reserve(ek_mbp, NTLM_HASH_SZ);
608 
609 	/* Set "ExportedSessionKey to NONCE(16) */
610 	(void) smb_get_urandom(ssp_st->ss_ssnkey, NTLM_HASH_SZ);
611 
612 	/* Set "EncryptedRandomSessionKey" to RC4(...) */
613 	err = smb_encrypt_RC4(encr_ssn_key, NTLM_HASH_SZ,
614 	    ssp_st->ss_kxkey, NTLM_HASH_SZ,
615 	    ssp_st->ss_ssnkey, NTLM_HASH_SZ);
616 
617 	return (err);
618 }
619 
620 /*
621  * ntlmssp_final
622  *
623  * Called after successful authentication.
624  * Save the session key.
625  */
626 int
ntlmssp_final(struct ssp_ctx * sp)627 ntlmssp_final(struct ssp_ctx *sp)
628 {
629 	struct smb_ctx *ctx = sp->smb_ctx;
630 	ntlmssp_state_t *ssp_st = sp->sp_private;
631 	int err = 0;
632 
633 	/*
634 	 * Update/save the session key.
635 	 */
636 	if (ctx->ct_ssnkey_buf != NULL) {
637 		free(ctx->ct_ssnkey_buf);
638 		ctx->ct_ssnkey_buf = NULL;
639 	}
640 	ctx->ct_ssnkey_buf = malloc(NTLM_HASH_SZ);
641 	if (ctx->ct_ssnkey_buf == NULL) {
642 		err = ENOMEM;
643 		goto out;
644 	}
645 	ctx->ct_ssnkey_len = NTLM_HASH_SZ;
646 	memcpy(ctx->ct_ssnkey_buf, ssp_st->ss_ssnkey, NTLM_HASH_SZ);
647 
648 out:
649 	return (err);
650 }
651 
652 /*
653  * ntlmssp_next_token
654  *
655  * See ssp.c: ssp_ctx_next_token
656  */
657 int
ntlmssp_next_token(struct ssp_ctx * sp,struct mbdata * in_mb,struct mbdata * out_mb)658 ntlmssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb,
659 	struct mbdata *out_mb)
660 {
661 	int err;
662 
663 	if (out_mb == NULL) {
664 		/* final call on successful auth. */
665 		err = ntlmssp_final(sp);
666 		goto out;
667 	}
668 
669 	/* Will build an ouptut token. */
670 	err = mb_init(out_mb);
671 	if (err)
672 		goto out;
673 
674 	/*
675 	 * When called with in_mb == NULL, it means
676 	 * this is the first call for this session,
677 	 * so put a Type 1 (initialize) token.
678 	 */
679 	if (in_mb == NULL) {
680 		err = ntlmssp_put_type1(sp, out_mb);
681 		goto out;
682 	}
683 
684 	/*
685 	 * This is not the first call, so
686 	 * parse the response token we received.
687 	 * It should be a Type 2 (challenge).
688 	 * Then put a Type 3 (authenticate)
689 	 */
690 	err = ntlmssp_get_type2(sp, in_mb);
691 	if (err)
692 		goto out;
693 
694 	err = ntlmssp_put_type3(sp, out_mb);
695 
696 out:
697 	if (err)
698 		DPRINT("ret: %d", err);
699 	return (err);
700 }
701 
702 /*
703  * ntlmssp_ctx_destroy
704  *
705  * Destroy mechanism-specific data.
706  */
707 void
ntlmssp_destroy(struct ssp_ctx * sp)708 ntlmssp_destroy(struct ssp_ctx *sp)
709 {
710 	ntlmssp_state_t *ssp_st;
711 
712 	ssp_st = sp->sp_private;
713 	if (ssp_st != NULL) {
714 		sp->sp_private = NULL;
715 		free(ssp_st->ss_target_name);
716 		m_freem(ssp_st->ss_target_info);
717 		free(ssp_st);
718 	}
719 }
720 
721 /*
722  * ntlmssp_init_clnt
723  *
724  * Initialize a new NTLMSSP client context.
725  */
726 int
ntlmssp_init_client(struct ssp_ctx * sp)727 ntlmssp_init_client(struct ssp_ctx *sp)
728 {
729 	ntlmssp_state_t *ssp_st;
730 	smb_ctx_t *ctx = sp->smb_ctx;
731 
732 	if ((ctx->ct_authflags &
733 	    (SMB_AT_NTLM2 | SMB_AT_NTLM1 | SMB_AT_ANON)) == 0) {
734 		DPRINT("No NTLM authflags");
735 		return (EINVAL);
736 	}
737 
738 	/* Get the client nonce. */
739 	(void) smb_get_urandom(ctx->ct_clnonce, NTLM_CHAL_SZ);
740 
741 	ssp_st = calloc(1, sizeof (*ssp_st));
742 	if (ssp_st == NULL)
743 		return (ENOMEM);
744 
745 	sp->sp_nexttok = ntlmssp_next_token;
746 	sp->sp_destroy = ntlmssp_destroy;
747 	sp->sp_private = ssp_st;
748 
749 	return (0);
750 }
751