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 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <pthread.h> 27 #include <string.h> 28 #include <strings.h> 29 #include <syslog.h> 30 #include <synch.h> 31 #include <sys/errno.h> 32 #include <sys/types.h> 33 #include <sys/socket.h> 34 #include <netinet/in.h> 35 #include <arpa/inet.h> 36 #include <smbsrv/wintypes.h> 37 #include <smbsrv/libsmbrdr.h> 38 #include <smbsrv/ntstatus.h> 39 #include <smbsrv/smb.h> 40 #include <smbrdr.h> 41 42 #define SMBRDR_ANON_USER "IPC$" 43 44 static int smbrdr_session_setupx(struct sdb_logon *); 45 static struct sdb_logon *smbrdr_logon_init(struct sdb_session *, char *, 46 uint8_t *); 47 static int smbrdr_authenticate(char *, char *, uint8_t *); 48 49 /* 50 * If the username is SMBRDR_ANON_USER, an anonymous session will be 51 * established. Otherwise, an authenticated session will be established 52 * based on the specified credentials. 53 */ 54 int 55 smbrdr_logon(char *domain_controller, char *domain, char *username) 56 { 57 uint8_t pwd_hash[SMBAUTH_HASH_SZ]; 58 int rc; 59 60 if (username == NULL || *username == 0) { 61 syslog(LOG_DEBUG, "smbrdr: no username"); 62 return (-1); 63 } 64 65 bzero(pwd_hash, SMBAUTH_HASH_SZ); 66 if (smb_strcasecmp(username, SMBRDR_ANON_USER, 0) != 0) { 67 smb_ipc_get_passwd(pwd_hash, SMBAUTH_HASH_SZ); 68 if (*pwd_hash == 0) { 69 syslog(LOG_DEBUG, "smbrdr: no password"); 70 return (-1); 71 } 72 } 73 74 if (smbrdr_negotiate(domain_controller, domain) != 0) { 75 syslog(LOG_DEBUG, "smbrdr: negotiate failed"); 76 return (-1); 77 } 78 79 rc = smbrdr_authenticate(domain_controller, username, pwd_hash); 80 return ((rc == AUTH_USER_GRANT) ? 0 : -1); 81 82 } 83 84 /* 85 * Get the user session key from an already open named pipe. 86 * The RPC library needs this. See ndr_rpc_get_ssnkey() 87 * 88 * Returns zero (success) or an errno. 89 */ 90 int 91 smbrdr_get_ssnkey(int fid, unsigned char *ssn_key, size_t key_len) 92 { 93 struct sdb_logon *logon; 94 struct sdb_session *session; 95 struct sdb_netuse *netuse; 96 struct sdb_ofile *ofile; 97 98 if (ssn_key == NULL || key_len < SMBAUTH_SESSION_KEY_SZ) 99 return (EINVAL); 100 101 ofile = smbrdr_ofile_get(fid); 102 if (ofile == NULL) 103 return (EBADF); 104 105 netuse = ofile->netuse; 106 session = netuse->session; 107 logon = &session->logon; 108 109 if (key_len > SMBAUTH_SESSION_KEY_SZ) 110 bzero(ssn_key, key_len); 111 bcopy(logon->ssn_key, ssn_key, 112 SMBAUTH_SESSION_KEY_SZ); 113 114 smbrdr_ofile_put(ofile); 115 return (0); 116 } 117 118 /* 119 * smbrdr_authenticate 120 * 121 * This is the entry point for logging a user onto the domain. The 122 * session structure should have been obtained via a successful call 123 * to smbrdr_smb_connect. We allocate a logon structure to hold the 124 * user details and attempt to logon using smbrdr_session_setupx. 125 * Note that we expect the password fields to have been encrypted 126 * before this call. 127 * 128 * On success, the logon structure will be returned. Otherwise a null 129 * pointer will be returned. 130 */ 131 static int 132 smbrdr_authenticate(char *server, char *username, uint8_t *pwd) 133 { 134 struct sdb_session *session; 135 struct sdb_logon *logon; 136 struct sdb_logon old_logon; 137 int ret; 138 139 session = smbrdr_session_lock(server, SDB_SLCK_WRITE); 140 if (session == NULL) { 141 syslog(LOG_DEBUG, "smbrdr_authenticate: %s: no session with %s", 142 username, server); 143 return (-1); 144 } 145 146 bzero(&old_logon, sizeof (struct sdb_logon)); 147 148 logon = &session->logon; 149 if (logon->type != SDB_LOGON_NONE) { 150 if (strcasecmp(logon->username, username) == 0) { 151 /* The requested user has already been logged in */ 152 smbrdr_session_unlock(session); 153 return ((logon->type == SDB_LOGON_GUEST) 154 ? AUTH_GUEST_GRANT : AUTH_USER_GRANT); 155 } 156 157 old_logon = *logon; 158 } 159 160 logon = smbrdr_logon_init(session, username, pwd); 161 162 if (logon == NULL) { 163 syslog(LOG_DEBUG, "smbrdr_authenticate: %s: %m", username); 164 smbrdr_session_unlock(session); 165 return (-1); 166 } 167 168 if (smbrdr_session_setupx(logon) < 0) { 169 free(logon); 170 smbrdr_session_unlock(session); 171 return (-1); 172 } 173 174 175 ret = (logon->type == SDB_LOGON_GUEST) 176 ? AUTH_GUEST_GRANT : AUTH_USER_GRANT; 177 178 session->logon = *logon; 179 free(logon); 180 181 if (old_logon.type != SDB_LOGON_NONE) 182 (void) smbrdr_logoffx(&old_logon); 183 184 smbrdr_session_unlock(session); 185 return (ret); 186 } 187 188 189 /* 190 * smbrdr_session_setupx 191 * 192 * Build and send an SMB session setup command. This is used to log a 193 * user onto the domain. See CIFS section 4.1.2. 194 * 195 * Returns 0 on success. Otherwise returns a -ve error code. 196 */ 197 static int 198 smbrdr_session_setupx(struct sdb_logon *logon) 199 { 200 struct sdb_session *session; 201 smb_hdr_t smb_hdr; 202 smbrdr_handle_t srh; 203 smb_msgbuf_t *mb; 204 char *native_os; 205 char *native_lanman; 206 unsigned short data_bytes; 207 unsigned short guest; 208 unsigned long capabilities; 209 unsigned short null_size; 210 size_t (*strlen_fn)(const char *s); 211 DWORD status; 212 int rc; 213 214 /* 215 * Paranoia check - we should never get this 216 * far without a valid session structure. 217 */ 218 if ((session = logon->session) == NULL) 219 return (-1); 220 221 if (session->remote_caps & CAP_UNICODE) { 222 strlen_fn = smb_wcequiv_strlen; 223 null_size = sizeof (smb_wchar_t); 224 session->smb_flags2 |= SMB_FLAGS2_UNICODE; 225 } else { 226 strlen_fn = strlen; 227 null_size = sizeof (char); 228 } 229 230 if (smbrdr_sign_init(session, logon) < 0) { 231 syslog(LOG_DEBUG, 232 "smbrdr_session_setupx: smbrdr_sign_init failed"); 233 return (-1); 234 } 235 236 status = smbrdr_request_init(&srh, SMB_COM_SESSION_SETUP_ANDX, 237 session, 0, 0); 238 239 if (status != NT_STATUS_SUCCESS) { 240 smbrdr_sign_fini(session); 241 syslog(LOG_DEBUG, "smbrdr_session_setupx: %s", 242 xlate_nt_status(status)); 243 return (-1); 244 } 245 mb = &srh.srh_mbuf; 246 247 /* 248 * Regardless of the server's capabilities or what's 249 * reported in smb_flags2, we should report our full 250 * capabilities. 251 */ 252 capabilities = CAP_UNICODE | CAP_NT_SMBS | CAP_STATUS32; 253 254 /* 255 * Compute the BCC for unicode or ASCII strings. 256 */ 257 data_bytes = logon->auth.ci_len + logon->auth.cs_len + null_size; 258 data_bytes += strlen_fn(session->native_os) + null_size; 259 data_bytes += strlen_fn(session->native_lanman) + null_size; 260 261 if (logon->type == SDB_LOGON_ANONYMOUS) { 262 /* 263 * Anonymous logon: no username or domain name. 264 * We still need to include two null characters. 265 */ 266 data_bytes += (2 * null_size); 267 268 rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllwlu.u.", 269 13, /* smb_wct */ 270 0xff, /* AndXCommand (none) */ 271 32 + 26 + 3 + data_bytes, /* AndXOffset */ 272 SMBRDR_REQ_BUFSZ, /* MaxBufferSize */ 273 1, /* MaxMpxCount */ 274 0, /* VcNumber */ 275 0, /* SessionKey */ 276 1, /* CaseInsensitivePassLength */ 277 0, /* CaseSensitivePassLength */ 278 0, /* Reserved */ 279 capabilities, /* Capabilities */ 280 data_bytes, /* smb_bcc */ 281 0, /* No user or domain */ 282 session->native_os, /* NativeOS */ 283 session->native_lanman); /* NativeLanMan */ 284 } else { 285 data_bytes += strlen_fn(logon->username) + null_size; 286 data_bytes += strlen_fn(session->domain) + null_size; 287 288 rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllw#c#cuuu.u.", 289 13, /* smb_wct */ 290 0xff, /* AndXCommand (none) */ 291 32 + 26 + 3 + data_bytes, /* AndXOffset */ 292 SMBRDR_REQ_BUFSZ, /* MaxBufferSize */ 293 1, /* MaxMpxCount */ 294 session->vc, /* VcNumber */ 295 session->sesskey, /* SessionKey */ 296 logon->auth.ci_len, /* CaseInsensitivePassLength */ 297 logon->auth.cs_len, /* CaseSensitivePassLength */ 298 0, /* Reserved */ 299 capabilities, /* Capabilities */ 300 data_bytes, /* smb_bcc */ 301 logon->auth.ci_len, /* ci length spec */ 302 logon->auth.ci, /* CaseInsensitivePassword */ 303 logon->auth.cs_len, /* cs length spec */ 304 logon->auth.cs, /* CaseSensitivePassword */ 305 logon->username, /* AccountName */ 306 session->domain, /* PrimaryDomain */ 307 session->native_os, /* NativeOS */ 308 session->native_lanman); /* NativeLanMan */ 309 } 310 311 if (rc <= 0) { 312 syslog(LOG_DEBUG, "smbrdr_session_setupx: encode failed"); 313 smbrdr_handle_free(&srh); 314 smbrdr_sign_fini(session); 315 return (-1); 316 } 317 318 status = smbrdr_exchange(&srh, &smb_hdr, 0); 319 if (status != NT_STATUS_SUCCESS) { 320 syslog(LOG_DEBUG, "smbrdr_session_setupx: %s", 321 xlate_nt_status(status)); 322 smbrdr_handle_free(&srh); 323 smbrdr_sign_fini(session); 324 return (-1); 325 } 326 327 rc = smb_msgbuf_decode(mb, "5.w2.u", &guest, &native_os); 328 329 /* 330 * There was a problem in decoding response from 331 * a Samba 2.x PDC. This server sends strings in ASCII 332 * format and there is one byte with value 0 between 333 * native_os and native_lm: 334 * 335 * FF 53 4D 42 73 00 .SMBs. 336 * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................ 337 * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 01 ......d......... 338 * 00 1C 00 55 6E 69 78 00 53 61 6D 62 61 20 32 2E ...Unix.Samba.2. 339 * 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 2.8a.SAMBA_DOM. 340 * 341 * The byte doesn't seem to be padding because when change in 342 * native OS from Unix to Unix1 the 0 byte is still there: 343 * 344 * FF 53 4D 42 73 00 .SMBs. 345 * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................ 346 * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 00 ......d......... 347 * 00 1D 00 55 6E 69 78 31 00 53 61 6D 62 61 20 32 ...Unix1.Samba.2 348 * 2E 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 .2.8a.SAMBA_DOM. 349 */ 350 if (rc > 0) { 351 if (session->remote_caps & CAP_UNICODE) 352 rc = smb_msgbuf_decode(mb, "u", &native_lanman); 353 else 354 rc = smb_msgbuf_decode(mb, ".u", &native_lanman); 355 } 356 357 if (rc <= 0) { 358 syslog(LOG_DEBUG, "smbrdr_session_setupx: decode failed"); 359 smbrdr_handle_free(&srh); 360 smbrdr_sign_fini(session); 361 return (-1); 362 } 363 364 session->remote_os = smbnative_os_value(native_os); 365 session->remote_lm = smbnative_lm_value(native_lanman); 366 session->pdc_type = smbnative_pdc_value(native_lanman); 367 368 logon->uid = smb_hdr.uid; 369 if (guest) 370 logon->type = SDB_LOGON_GUEST; 371 372 smbrdr_handle_free(&srh); 373 smbrdr_sign_unset_key(session); 374 375 logon->state = SDB_LSTATE_SETUP; 376 377 return (0); 378 } 379 380 /* 381 * smbrdr_logoffx 382 * 383 * Build and send an SMB session logoff (SMB_COM_LOGOFF_ANDX) command. 384 * This is the inverse of an SMB_COM_SESSION_SETUP_ANDX. See CIFS 385 * section 4.1.3. The logon structure should have been obtained from a 386 * successful call to smbrdr_authenticate. 387 * 388 * Returns 0 on success. Otherwise returns a -ve error code. 389 */ 390 int 391 smbrdr_logoffx(struct sdb_logon *logon) 392 { 393 struct sdb_session *session; 394 smbrdr_handle_t srh; 395 smb_hdr_t smb_hdr; 396 DWORD status; 397 int rc; 398 399 if (logon->state != SDB_LSTATE_SETUP) { 400 /* No user to logoff */ 401 bzero(logon, sizeof (struct sdb_logon)); 402 return (0); 403 } 404 405 if ((session = logon->session) == 0) { 406 bzero(logon, sizeof (struct sdb_logon)); 407 return (0); 408 } 409 410 logon->state = SDB_LSTATE_LOGGING_OFF; 411 smbrdr_netuse_logoff(logon->uid); 412 413 if ((session->state != SDB_SSTATE_NEGOTIATED) && 414 (session->state != SDB_SSTATE_DISCONNECTING)) { 415 bzero(logon, sizeof (struct sdb_logon)); 416 return (0); 417 } 418 419 status = smbrdr_request_init(&srh, SMB_COM_LOGOFF_ANDX, 420 session, logon, 0); 421 422 if (status != NT_STATUS_SUCCESS) { 423 logon->state = SDB_LSTATE_SETUP; 424 syslog(LOG_DEBUG, "smbrdr_logoffx: %s: %s", logon->username, 425 xlate_nt_status(status)); 426 return (-1); 427 } 428 429 rc = smb_msgbuf_encode(&srh.srh_mbuf, "bbbww", 2, 0xff, 0, 0, 0); 430 if (rc < 0) { 431 logon->state = SDB_LSTATE_SETUP; 432 smbrdr_handle_free(&srh); 433 syslog(LOG_DEBUG, "smbrdr_logoffx: %s: encode failed", 434 logon->username); 435 return (rc); 436 } 437 438 status = smbrdr_exchange(&srh, &smb_hdr, 0); 439 if (status != NT_STATUS_SUCCESS) { 440 syslog(LOG_DEBUG, "smbrdr_logoffx: %s: %s", logon->username, 441 xlate_nt_status(status)); 442 rc = -1; 443 } else { 444 rc = 0; 445 } 446 447 bzero(logon, sizeof (struct sdb_logon)); 448 smbrdr_handle_free(&srh); 449 return (rc); 450 } 451 452 453 /* 454 * smbrdr_logon_init 455 * 456 * Find a slot for account logon information. The account information 457 * is associated with a session so we need a valid session slot before 458 * calling this function. If we already have a record of the specified 459 * account, a pointer to that record is returned. Otherwise we attempt 460 * to allocate a new one. 461 */ 462 static struct sdb_logon * 463 smbrdr_logon_init(struct sdb_session *session, char *username, uint8_t *pwd) 464 { 465 struct sdb_logon *logon; 466 int64_t smbrdr_lmcompl; 467 int rc; 468 469 logon = (struct sdb_logon *)malloc(sizeof (sdb_logon_t)); 470 if (logon == 0) 471 return (0); 472 473 bzero(logon, sizeof (struct sdb_logon)); 474 logon->session = session; 475 476 (void) smb_config_getnum(SMB_CI_LM_LEVEL, &smbrdr_lmcompl); 477 478 if (strcmp(username, "IPC$") == 0) { 479 logon->type = SDB_LOGON_ANONYMOUS; 480 logon->auth.ci_len = 1; 481 *(logon->auth.ci) = 0; 482 logon->auth.cs_len = 0; 483 } else { 484 logon->type = SDB_LOGON_USER; 485 rc = smb_auth_set_info(username, 0, pwd, 486 session->domain, session->challenge_key, 487 session->challenge_len, smbrdr_lmcompl, &logon->auth); 488 489 /* Generate (and save) the session key. */ 490 if (rc == 0) { 491 rc = smb_auth_gen_session_key(&logon->auth, 492 logon->ssn_key); 493 } 494 495 if (rc != 0) { 496 free(logon); 497 return (0); 498 } 499 } 500 501 (void) strlcpy(logon->username, username, MAX_ACCOUNT_NAME); 502 logon->state = SDB_LSTATE_INIT; 503 return (logon); 504 } 505