xref: /illumos-gate/usr/src/lib/libsmbfs/smb/ntlmssp.c (revision 4a3b1d5b615ff6e54da1cc17f331e1ac794c5191)
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 2011 Nexenta Systems, Inc.  All rights reserved.
24  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. 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 "spnego.h"
63 #include "derparse.h"
64 #include "ssp.h"
65 #include "ntlm.h"
66 #include "ntlmssp.h"
67 
68 typedef struct ntlmssp_state {
69 	uint32_t ss_flags;
70 	char *ss_target_name;
71 	struct mbuf *ss_target_info;
72 } ntlmssp_state_t;
73 
74 /*
75  * So called "security buffer".
76  * A lot like an RPC string.
77  */
78 struct sec_buf {
79 	uint16_t sb_length;
80 	uint16_t sb_maxlen;
81 	uint32_t sb_offset;
82 };
83 #define	ID_SZ 8
84 static const char ntlmssp_id[ID_SZ] = "NTLMSSP";
85 
86 /*
87  * Get a "security buffer" (header part)
88  */
89 static int
90 md_get_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
91 {
92 	int err;
93 
94 	(void) md_get_uint16le(mbp, &sb->sb_length);
95 	(void) md_get_uint16le(mbp, &sb->sb_maxlen);
96 	err = md_get_uint32le(mbp, &sb->sb_offset);
97 
98 	return (err);
99 }
100 
101 /*
102  * Get a "security buffer" (data part), where
103  * the data is delivered as an mbuf.
104  */
105 static int
106 md_get_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf **mp)
107 {
108 	struct mbdata tmp_mb;
109 	int err;
110 
111 	/*
112 	 * Setup tmp_mb to point to the start of the header.
113 	 * This is a dup ref - do NOT free it.
114 	 */
115 	mb_initm(&tmp_mb, mbp->mb_top);
116 
117 	/* Skip data up to the offset. */
118 	err = md_get_mem(&tmp_mb, NULL, sb->sb_offset, MB_MSYSTEM);
119 	if (err)
120 		return (err);
121 
122 	/* Get the data (as an mbuf). */
123 	err = md_get_mbuf(&tmp_mb, sb->sb_maxlen, mp);
124 
125 	return (err);
126 }
127 
128 /*
129  * Put a "security buffer" (header part)
130  */
131 static int
132 mb_put_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
133 {
134 	int err;
135 
136 	(void) mb_put_uint16le(mbp, sb->sb_length);
137 	(void) mb_put_uint16le(mbp, sb->sb_maxlen);
138 	err = mb_put_uint32le(mbp, sb->sb_offset);
139 
140 	return (err);
141 }
142 
143 /*
144  * Put a "security buffer" (data part), where
145  * the data is an mbuf.  Note: consumes m.
146  */
147 static int
148 mb_put_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf *m)
149 {
150 	int cnt0, err;
151 
152 	sb->sb_offset = cnt0 = mbp->mb_count;
153 	err = mb_put_mbuf(mbp, m);
154 	sb->sb_maxlen = sb->sb_length = mbp->mb_count - cnt0;
155 
156 	return (err);
157 }
158 
159 /*
160  * Put a "security buffer" (data part), where
161  * the data is a string (OEM or unicode).
162  *
163  * The string is NOT null terminated.
164  */
165 static int
166 mb_put_sb_string(struct mbdata *mbp, struct sec_buf *sb,
167 	const char *s, int unicode)
168 {
169 	int err, trim;
170 	struct mbdata tmp_mb;
171 
172 	/*
173 	 * Put the string into a temp. mbuf,
174 	 * then chop off the null terminator
175 	 * before appending to caller's mbp.
176 	 */
177 	err = mb_init(&tmp_mb);
178 	if (err)
179 		return (err);
180 	err = mb_put_string(&tmp_mb, s, unicode);
181 	if (err)
182 		return (err);
183 
184 	trim = (unicode) ? 2 : 1;
185 	if (tmp_mb.mb_cur->m_len < trim)
186 		return (EFAULT);
187 	tmp_mb.mb_cur->m_len -= trim;
188 
189 	err = mb_put_sb_data(mbp, sb, tmp_mb.mb_top);
190 	/*
191 	 * Note: tmp_mb.mb_top is consumed,
192 	 * so do NOT free it (no mb_done)
193 	 */
194 	return (err);
195 }
196 
197 /*
198  * Build a Type 1 message
199  *
200  * This message has a header section containing offsets to
201  * data later in the message.  We use the common trick of
202  * building it in two parts and then concatenatening.
203  */
204 int
205 ntlmssp_put_type1(struct ssp_ctx *sp, struct mbdata *out_mb)
206 {
207 	struct type1hdr {
208 		char h_id[ID_SZ];
209 		uint32_t h_type;
210 		uint32_t h_flags;
211 		struct sec_buf h_cldom;
212 		struct sec_buf h_wksta;
213 	} hdr;
214 	struct mbdata mb2;	/* 2nd part */
215 	int err;
216 	struct smb_ctx *ctx = sp->smb_ctx;
217 	ntlmssp_state_t *ssp_st = sp->sp_private;
218 	char *ucdom = NULL;
219 	char *ucwks = NULL;
220 
221 	if ((err = mb_init(&mb2)) != 0)
222 		return (err);
223 	mb2.mb_count = sizeof (hdr);
224 
225 	/*
226 	 * Initialize the negotiation flags, and
227 	 * save what we sent.  For reference:
228 	 * [MS-NLMP] spec. (also ntlmssp.h)
229 	 */
230 	ssp_st->ss_flags =
231 	    NTLMSSP_REQUEST_TARGET |
232 	    NTLMSSP_NEGOTIATE_NTLM |
233 	    NTLMSSP_NEGOTIATE_TARGET_INFO |
234 	    NTLMSSP_NEGOTIATE_128 |
235 	    NTLMSSP_NEGOTIATE_56;
236 
237 	if (ctx->ct_hflags2 & SMB_FLAGS2_UNICODE)
238 		ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_UNICODE;
239 	else
240 		ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_OEM;
241 
242 	if (ctx->ct_vcflags & SMBV_WILL_SIGN) {
243 		ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
244 		ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
245 	}
246 
247 	if (ctx->ct_authflags & SMB_AT_NTLM2)
248 		ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY;
249 	if (ctx->ct_authflags & SMB_AT_NTLM1)
250 		ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
251 
252 	bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
253 	hdr.h_type = 1; /* Type 1 */
254 	hdr.h_flags = ssp_st->ss_flags;
255 
256 	/*
257 	 * Put the client domain, client name strings.
258 	 * These are always in OEM format, upper-case.
259 	 */
260 	ucdom  = utf8_str_toupper(ctx->ct_domain);
261 	ucwks  = utf8_str_toupper(ctx->ct_locname);
262 	if (ucdom == NULL || ucwks == NULL) {
263 		err = ENOMEM;
264 		goto out;
265 	}
266 	err = mb_put_sb_string(&mb2, &hdr.h_cldom, ucdom, 0);
267 	if (err)
268 		goto out;
269 	err = mb_put_sb_string(&mb2, &hdr.h_wksta, ucwks, 0);
270 	if (err)
271 		goto out;
272 
273 	/*
274 	 * Marshal the header (in LE order)
275 	 * then concatenate the 2nd part.
276 	 */
277 	(void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
278 	(void) mb_put_uint32le(out_mb, hdr.h_type);
279 	(void) mb_put_uint32le(out_mb, hdr.h_flags);
280 	(void) mb_put_sb_hdr(out_mb, &hdr.h_cldom);
281 	(void) mb_put_sb_hdr(out_mb, &hdr.h_wksta);
282 
283 	err = mb_put_mbuf(out_mb, mb2.mb_top);
284 
285 out:
286 	free(ucdom);
287 	free(ucwks);
288 
289 	return (err);
290 }
291 
292 /*
293  * Parse a Type 2 message
294  */
295 int
296 ntlmssp_get_type2(struct ssp_ctx *sp, struct mbdata *in_mb)
297 {
298 	struct type2hdr {
299 		char h_id[ID_SZ];
300 		uint32_t h_type;
301 		struct sec_buf h_target_name;
302 		uint32_t h_flags;
303 		uint8_t h_challenge[8];
304 		uint32_t h_context[2];		/* optional */
305 		struct sec_buf h_target_info;	/* optional */
306 	} hdr;
307 	struct mbdata top_mb, tmp_mb;
308 	struct mbuf *m;
309 	int err, uc;
310 	int min_hdr_sz = offsetof(struct type2hdr, h_context);
311 	struct smb_ctx *ctx = sp->smb_ctx;
312 	ntlmssp_state_t *ssp_st = sp->sp_private;
313 	char *buf = NULL;
314 
315 	if (m_totlen(in_mb->mb_top) < min_hdr_sz) {
316 		err = EBADRPC;
317 		goto out;
318 	}
319 
320 	/*
321 	 * Save the mbdata pointers before we consume anything.
322 	 * Careful to NOT free this (would be dup. free)
323 	 * We use this below to find data based on offsets
324 	 * from the start of the header.
325 	 */
326 	top_mb = *in_mb;
327 
328 	/* Parse the fixed size header stuff. */
329 	bzero(&hdr, sizeof (hdr));
330 	(void) md_get_mem(in_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
331 	(void) md_get_uint32le(in_mb, &hdr.h_type);
332 	if (hdr.h_type != 2) {
333 		err = EPROTO;
334 		goto out;
335 	}
336 	(void) md_get_sb_hdr(in_mb, &hdr.h_target_name);
337 	(void) md_get_uint32le(in_mb, &hdr.h_flags);
338 	(void) md_get_mem(in_mb, &hdr.h_challenge, NTLM_CHAL_SZ, MB_MSYSTEM);
339 
340 	/*
341 	 * Save flags, challenge for later.
342 	 */
343 	ssp_st->ss_flags = hdr.h_flags;
344 	uc = hdr.h_flags & NTLMSSP_NEGOTIATE_UNICODE;
345 	bcopy(&hdr.h_challenge, ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
346 
347 	/*
348 	 * Now find out if the optional parts are there.
349 	 */
350 	if ((m_totlen(top_mb.mb_top) > sizeof (hdr)) &&
351 	    (hdr.h_target_name.sb_offset >= sizeof (hdr))) {
352 		(void) md_get_uint32le(in_mb, &hdr.h_context[0]);
353 		(void) md_get_uint32le(in_mb, &hdr.h_context[1]);
354 		(void) md_get_sb_hdr(in_mb, &hdr.h_target_info);
355 	}
356 
357 	/*
358 	 * Get the target name string.  First get a copy of
359 	 * the data from the offset/length indicated in the
360 	 * security buffer header; then parse the string.
361 	 */
362 	err = md_get_sb_data(&top_mb, &hdr.h_target_name, &m);
363 	if (err)
364 		goto out;
365 	mb_initm(&tmp_mb, m);
366 	err = md_get_string(&tmp_mb, &ssp_st->ss_target_name, uc);
367 	mb_done(&tmp_mb);
368 
369 	/*
370 	 * Get the target info blob, if present.
371 	 */
372 	if (hdr.h_target_info.sb_offset >= sizeof (hdr)) {
373 		err = md_get_sb_data(&top_mb, &hdr.h_target_info,
374 		    &ssp_st->ss_target_info);
375 	}
376 
377 out:
378 	if (buf != NULL)
379 		free(buf);
380 
381 	return (err);
382 }
383 
384 /*
385  * Build a Type 3 message
386  *
387  * This message has a header section containing offsets to
388  * data later in the message.  We use the common trick of
389  * building it in two parts and then concatenatening.
390  */
391 int
392 ntlmssp_put_type3(struct ssp_ctx *sp, struct mbdata *out_mb)
393 {
394 	struct type3hdr {
395 		char h_id[ID_SZ];
396 		uint32_t h_type;
397 		struct sec_buf h_lm_resp;
398 		struct sec_buf h_nt_resp;
399 		struct sec_buf h_domain;
400 		struct sec_buf h_user;
401 		struct sec_buf h_wksta;
402 		struct sec_buf h_ssn_key;
403 		uint32_t h_flags;
404 	} hdr;
405 	struct mbdata lm_mbc;	/* LM response */
406 	struct mbdata nt_mbc;	/* NT response */
407 	struct mbdata ti_mbc;	/* target info */
408 	struct mbdata mb2;	/* payload */
409 	int err, uc;
410 	struct smb_ctx *ctx = sp->smb_ctx;
411 	ntlmssp_state_t *ssp_st = sp->sp_private;
412 
413 	bzero(&hdr, sizeof (hdr));
414 	bzero(&lm_mbc, sizeof (lm_mbc));
415 	bzero(&nt_mbc, sizeof (nt_mbc));
416 	bzero(&ti_mbc, sizeof (ti_mbc));
417 	bzero(&mb2, sizeof (mb2));
418 
419 	/*
420 	 * Fill in the NTLMSSP header, etc.
421 	 */
422 	if ((err = mb_init(&mb2)) != 0)
423 		goto out;
424 	mb2.mb_count = sizeof (hdr);
425 	uc = ssp_st->ss_flags & NTLMSSP_NEGOTIATE_UNICODE;
426 
427 	bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
428 	hdr.h_type = 3; /* Type 3 */
429 	hdr.h_flags = ssp_st->ss_flags;
430 
431 	/*
432 	 * Put the LMv2,NTLMv2 responses, or
433 	 * possibly LM, NTLM (v1) responses.
434 	 */
435 	if (ctx->ct_authflags & SMB_AT_NTLM2) {
436 		/* Build the NTLMv2 "target info" blob. */
437 		err = ntlm_build_target_info(ctx,
438 		    ssp_st->ss_target_info, &ti_mbc);
439 		if (err)
440 			goto out;
441 		err = ntlm_put_v2_responses(ctx, &ti_mbc,
442 		    &lm_mbc, &nt_mbc);
443 	} else {
444 		err = ntlm_put_v1_responses(ctx,
445 		    &lm_mbc, &nt_mbc);
446 	}
447 	if (err)
448 		goto out;
449 
450 	err = mb_put_sb_data(&mb2, &hdr.h_lm_resp, lm_mbc.mb_top);
451 	lm_mbc.mb_top = NULL; /* consumed */
452 	if (err)
453 		goto out;
454 	err = mb_put_sb_data(&mb2, &hdr.h_nt_resp, nt_mbc.mb_top);
455 	nt_mbc.mb_top = NULL; /* consumed */
456 	if (err)
457 		goto out;
458 
459 	/*
460 	 * Put the "target" (domain), user, workstation
461 	 */
462 	err = mb_put_sb_string(&mb2, &hdr.h_domain, ctx->ct_domain, uc);
463 	if (err)
464 		goto out;
465 	err = mb_put_sb_string(&mb2, &hdr.h_user, ctx->ct_user, uc);
466 	if (err)
467 		goto out;
468 	err = mb_put_sb_string(&mb2, &hdr.h_wksta, ctx->ct_locname, uc);
469 	if (err)
470 		goto out;
471 
472 	/*
473 	 * Put the "Random Session Key".  We don't set
474 	 * NTLMSSP_NEGOTIATE_KEY_EXCH, so it's empty.
475 	 * (In-line mb_put_sb_data here.)
476 	 */
477 	hdr.h_ssn_key.sb_maxlen = hdr.h_ssn_key.sb_length = 0;
478 	hdr.h_ssn_key.sb_offset = mb2.mb_count;
479 
480 	/*
481 	 * Marshal the header (in LE order)
482 	 * then concatenate the 2nd part.
483 	 */
484 	(void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
485 	(void) mb_put_uint32le(out_mb, hdr.h_type);
486 
487 	(void) mb_put_sb_hdr(out_mb, &hdr.h_lm_resp);
488 	(void) mb_put_sb_hdr(out_mb, &hdr.h_nt_resp);
489 
490 	(void) mb_put_sb_hdr(out_mb, &hdr.h_domain);
491 	(void) mb_put_sb_hdr(out_mb, &hdr.h_user);
492 	(void) mb_put_sb_hdr(out_mb, &hdr.h_wksta);
493 
494 	(void) mb_put_sb_hdr(out_mb, &hdr.h_ssn_key);
495 	(void) mb_put_uint32le(out_mb, hdr.h_flags);
496 
497 	err = mb_put_mbuf(out_mb, mb2.mb_top);
498 	mb2.mb_top = NULL; /* consumed */
499 
500 out:
501 	mb_done(&mb2);
502 	mb_done(&lm_mbc);
503 	mb_done(&nt_mbc);
504 	mb_done(&ti_mbc);
505 
506 	return (err);
507 }
508 
509 /*
510  * ntlmssp_final
511  *
512  * Called after successful authentication.
513  * Setup the MAC key for signing.
514  */
515 int
516 ntlmssp_final(struct ssp_ctx *sp)
517 {
518 	struct smb_ctx *ctx = sp->smb_ctx;
519 	int err = 0;
520 
521 	/*
522 	 * MAC_key is just the session key, but
523 	 * Only on the first successful auth.
524 	 */
525 	if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) &&
526 	    (ctx->ct_mackey == NULL)) {
527 		ctx->ct_mackeylen = NTLM_HASH_SZ;
528 		ctx->ct_mackey = malloc(ctx->ct_mackeylen);
529 		if (ctx->ct_mackey == NULL) {
530 			ctx->ct_mackeylen = 0;
531 			err = ENOMEM;
532 			goto out;
533 		}
534 		memcpy(ctx->ct_mackey, ctx->ct_ssn_key, NTLM_HASH_SZ);
535 		/*
536 		 * Apparently, the server used seq. no. zero
537 		 * for our previous message, so next is two.
538 		 */
539 		ctx->ct_mac_seqno = 2;
540 	}
541 
542 out:
543 	return (err);
544 }
545 
546 /*
547  * ntlmssp_next_token
548  *
549  * See ssp.c: ssp_ctx_next_token
550  */
551 int
552 ntlmssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb,
553 	struct mbdata *out_mb)
554 {
555 	int err;
556 
557 	if (out_mb == NULL) {
558 		/* final call on successful auth. */
559 		err = ntlmssp_final(sp);
560 		goto out;
561 	}
562 
563 	/* Will build an ouptut token. */
564 	err = mb_init(out_mb);
565 	if (err)
566 		goto out;
567 
568 	/*
569 	 * When called with in_mb == NULL, it means
570 	 * this is the first call for this session,
571 	 * so put a Type 1 (initialize) token.
572 	 */
573 	if (in_mb == NULL) {
574 		err = ntlmssp_put_type1(sp, out_mb);
575 		goto out;
576 	}
577 
578 	/*
579 	 * This is not the first call, so
580 	 * parse the response token we received.
581 	 * It should be a Type 2 (challenge).
582 	 * Then put a Type 3 (authenticate)
583 	 */
584 	err = ntlmssp_get_type2(sp, in_mb);
585 	if (err)
586 		goto out;
587 
588 	err = ntlmssp_put_type3(sp, out_mb);
589 
590 out:
591 	if (err)
592 		DPRINT("ret: %d", err);
593 	return (err);
594 }
595 
596 /*
597  * ntlmssp_ctx_destroy
598  *
599  * Destroy mechanism-specific data.
600  */
601 void
602 ntlmssp_destroy(struct ssp_ctx *sp)
603 {
604 	ntlmssp_state_t *ssp_st;
605 
606 	ssp_st = sp->sp_private;
607 	if (ssp_st != NULL) {
608 		sp->sp_private = NULL;
609 		free(ssp_st->ss_target_name);
610 		m_freem(ssp_st->ss_target_info);
611 		free(ssp_st);
612 	}
613 }
614 
615 /*
616  * ntlmssp_init_clnt
617  *
618  * Initialize a new NTLMSSP client context.
619  */
620 int
621 ntlmssp_init_client(struct ssp_ctx *sp)
622 {
623 	ntlmssp_state_t *ssp_st;
624 
625 	if ((sp->smb_ctx->ct_authflags &
626 	    (SMB_AT_NTLM2 | SMB_AT_NTLM1)) == 0) {
627 		DPRINT("No NTLM authflags");
628 		return (ENOTSUP);
629 	}
630 
631 	ssp_st = calloc(1, sizeof (*ssp_st));
632 	if (ssp_st == NULL)
633 		return (ENOMEM);
634 
635 	sp->sp_nexttok = ntlmssp_next_token;
636 	sp->sp_destroy = ntlmssp_destroy;
637 	sp->sp_private = ssp_st;
638 
639 	return (0);
640 }
641