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