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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 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 bcopy(ntlmssp_id, &hdr.h_id, ID_SZ); 248 hdr.h_type = 1; /* Type 1 */ 249 hdr.h_flags = ssp_st->ss_flags; 250 251 /* 252 * Put the client domain, client name strings. 253 * These are always in OEM format, upper-case. 254 */ 255 ucdom = utf8_str_toupper(ctx->ct_domain); 256 ucwks = utf8_str_toupper(ctx->ct_locname); 257 if (ucdom == NULL || ucwks == NULL) { 258 err = ENOMEM; 259 goto out; 260 } 261 err = mb_put_sb_string(&mb2, &hdr.h_cldom, ucdom, 0); 262 if (err) 263 goto out; 264 err = mb_put_sb_string(&mb2, &hdr.h_wksta, ucwks, 0); 265 if (err) 266 goto out; 267 268 /* 269 * Marshal the header (in LE order) 270 * then concatenate the 2nd part. 271 */ 272 (void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM); 273 (void) mb_put_uint32le(out_mb, hdr.h_type); 274 (void) mb_put_uint32le(out_mb, hdr.h_flags); 275 (void) mb_put_sb_hdr(out_mb, &hdr.h_cldom); 276 (void) mb_put_sb_hdr(out_mb, &hdr.h_wksta); 277 278 err = mb_put_mbuf(out_mb, mb2.mb_top); 279 280 out: 281 free(ucdom); 282 free(ucwks); 283 284 return (err); 285 } 286 287 /* 288 * Parse a Type 2 message 289 */ 290 int 291 ntlmssp_get_type2(struct ssp_ctx *sp, struct mbdata *in_mb) 292 { 293 struct type2hdr { 294 char h_id[ID_SZ]; 295 uint32_t h_type; 296 struct sec_buf h_target_name; 297 uint32_t h_flags; 298 uint8_t h_challenge[8]; 299 uint32_t h_context[2]; /* optional */ 300 struct sec_buf h_target_info; /* optional */ 301 } hdr; 302 struct mbdata top_mb, tmp_mb; 303 struct mbuf *m; 304 int err, uc; 305 int min_hdr_sz = offsetof(struct type2hdr, h_context); 306 struct smb_ctx *ctx = sp->smb_ctx; 307 ntlmssp_state_t *ssp_st = sp->sp_private; 308 char *buf = NULL; 309 310 if (m_totlen(in_mb->mb_top) < min_hdr_sz) { 311 err = EBADRPC; 312 goto out; 313 } 314 315 /* 316 * Save the mbdata pointers before we consume anything. 317 * Careful to NOT free this (would be dup. free) 318 * We use this below to find data based on offsets 319 * from the start of the header. 320 */ 321 top_mb = *in_mb; 322 323 /* Parse the fixed size header stuff. */ 324 bzero(&hdr, sizeof (hdr)); 325 (void) md_get_mem(in_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM); 326 (void) md_get_uint32le(in_mb, &hdr.h_type); 327 if (hdr.h_type != 2) { 328 err = EPROTO; 329 goto out; 330 } 331 (void) md_get_sb_hdr(in_mb, &hdr.h_target_name); 332 (void) md_get_uint32le(in_mb, &hdr.h_flags); 333 (void) md_get_mem(in_mb, &hdr.h_challenge, NTLM_CHAL_SZ, MB_MSYSTEM); 334 335 /* 336 * Save flags, challenge for later. 337 */ 338 ssp_st->ss_flags = hdr.h_flags; 339 uc = hdr.h_flags & NTLMSSP_NEGOTIATE_UNICODE; 340 bcopy(&hdr.h_challenge, ctx->ct_ntlm_chal, NTLM_CHAL_SZ); 341 342 /* 343 * Now find out if the optional parts are there. 344 */ 345 if ((m_totlen(top_mb.mb_top) > sizeof (hdr)) && 346 (hdr.h_target_name.sb_offset >= sizeof (hdr))) { 347 (void) md_get_uint32le(in_mb, &hdr.h_context[0]); 348 (void) md_get_uint32le(in_mb, &hdr.h_context[1]); 349 (void) md_get_sb_hdr(in_mb, &hdr.h_target_info); 350 } 351 352 /* 353 * Get the target name string. First get a copy of 354 * the data from the offset/length indicated in the 355 * security buffer header; then parse the string. 356 */ 357 err = md_get_sb_data(&top_mb, &hdr.h_target_name, &m); 358 if (err) 359 goto out; 360 mb_initm(&tmp_mb, m); 361 err = md_get_string(&tmp_mb, &ssp_st->ss_target_name, uc); 362 mb_done(&tmp_mb); 363 364 /* 365 * Get the target info blob, if present. 366 */ 367 if (hdr.h_target_info.sb_offset >= sizeof (hdr)) { 368 err = md_get_sb_data(&top_mb, &hdr.h_target_info, 369 &ssp_st->ss_target_info); 370 } 371 372 out: 373 if (buf != NULL) 374 free(buf); 375 376 return (err); 377 } 378 379 /* 380 * Build a Type 3 message 381 * 382 * This message has a header section containing offsets to 383 * data later in the message. We use the common trick of 384 * building it in two parts and then concatenatening. 385 */ 386 int 387 ntlmssp_put_type3(struct ssp_ctx *sp, struct mbdata *out_mb) 388 { 389 struct type3hdr { 390 char h_id[ID_SZ]; 391 uint32_t h_type; 392 struct sec_buf h_lm_resp; 393 struct sec_buf h_nt_resp; 394 struct sec_buf h_domain; 395 struct sec_buf h_user; 396 struct sec_buf h_wksta; 397 } hdr; 398 struct mbdata lm_mbc, nt_mbc, ti_mbc; 399 struct mbdata mb2; /* 2nd part */ 400 int err, uc; 401 char *ucdom = NULL; /* user's domain */ 402 char *ucuser = NULL; /* user name */ 403 char *ucwksta = NULL; /* workstation */ 404 struct smb_ctx *ctx = sp->smb_ctx; 405 ntlmssp_state_t *ssp_st = sp->sp_private; 406 407 bzero(&lm_mbc, sizeof (lm_mbc)); 408 bzero(&nt_mbc, sizeof (nt_mbc)); 409 bzero(&ti_mbc, sizeof (ti_mbc)); 410 bzero(&mb2, sizeof (mb2)); 411 412 /* 413 * Convert the user name to upper-case, as that's what's 414 * used when computing LMv2 and NTLMv2 responses. Also 415 * domain, workstation 416 */ 417 ucdom = utf8_str_toupper(ctx->ct_domain); 418 ucuser = utf8_str_toupper(ctx->ct_user); 419 ucwksta = utf8_str_toupper(ctx->ct_locname); 420 if (ucdom == NULL || ucuser == NULL || ucwksta == NULL) { 421 err = ENOMEM; 422 goto out; 423 } 424 425 if ((err = mb_init(&mb2)) != 0) 426 goto out; 427 mb2.mb_count = sizeof (hdr); 428 uc = ssp_st->ss_flags & NTLMSSP_NEGOTIATE_UNICODE; 429 430 bcopy(ntlmssp_id, &hdr.h_id, ID_SZ); 431 hdr.h_type = 3; /* Type 3 */ 432 433 /* 434 * Put the LMv2,NTLMv2 responses, or 435 * possibly LM, NTLM (v1) responses. 436 */ 437 if (ctx->ct_authflags & SMB_AT_NTLM2) { 438 /* Build the NTLMv2 "target info" blob. */ 439 err = ntlm_build_target_info(ctx, 440 ssp_st->ss_target_info, &ti_mbc); 441 if (err) 442 goto out; 443 err = ntlm_put_v2_responses(ctx, &ti_mbc, 444 &lm_mbc, &nt_mbc); 445 } else { 446 err = ntlm_put_v1_responses(ctx, 447 &lm_mbc, &nt_mbc); 448 } 449 if (err) 450 goto out; 451 452 err = mb_put_sb_data(&mb2, &hdr.h_lm_resp, lm_mbc.mb_top); 453 lm_mbc.mb_top = NULL; /* consumed */ 454 if (err) 455 goto out; 456 err = mb_put_sb_data(&mb2, &hdr.h_nt_resp, nt_mbc.mb_top); 457 nt_mbc.mb_top = NULL; /* consumed */ 458 if (err) 459 goto out; 460 461 /* 462 * Put the "target" (domain), user, workstation 463 */ 464 err = mb_put_sb_string(&mb2, &hdr.h_domain, ucdom, uc); 465 if (err) 466 goto out; 467 err = mb_put_sb_string(&mb2, &hdr.h_user, ucuser, uc); 468 if (err) 469 goto out; 470 err = mb_put_sb_string(&mb2, &hdr.h_wksta, ucwksta, uc); 471 if (err) 472 goto out; 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 err = mb_put_mbuf(out_mb, mb2.mb_top); 489 mb2.mb_top = NULL; /* consumed */ 490 491 out: 492 free(ucdom); 493 free(ucuser); 494 free(ucwksta); 495 496 mb_done(&mb2); 497 mb_done(&lm_mbc); 498 mb_done(&nt_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