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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <errno.h>
  28 #include <synch.h>
  29 #include <stdio.h>
  30 #include <stdlib.h>
  31 #include <unistd.h>
  32 #include <string.h>
  33 #include <strings.h>
  34 #include <syslog.h>
  35 #include <fcntl.h>
  36 #include <bsm/adt.h>
  37 #include <bsm/adt_event.h>
  38 #include <bsm/audit_uevents.h>
  39 #include <pwd.h>
  40 #include <nss_dbdefs.h>
  41 #include <sys/idmap.h>
  42 #include "smbd.h"
  43 
  44 
  45 /*
  46  * An audit session is established at user logon and terminated at user
  47  * logoff.
  48  *
  49  * SMB audit handles are allocated when users logon (SmbSessionSetupX)
  50  * and deallocted when a user logs off (SmbLogoffX).  Each time an SMB
  51  * audit handle is allocated it is added to a global list.
  52  */
  53 typedef struct smb_audit {
  54         struct smb_audit *sa_next;
  55         adt_session_data_t *sa_handle;
  56         uid_t sa_uid;
  57         gid_t sa_gid;
  58         uint32_t sa_audit_sid;
  59         uint32_t sa_refcnt;
  60         char *sa_domain;
  61         char *sa_username;
  62 } smb_audit_t;
  63 
  64 static smb_audit_t *smbd_audit_list;
  65 static mutex_t smbd_audit_lock;
  66 
  67 /*
  68  * Unique identifier for audit sessions in the audit list.
  69  * Used to lookup an audit session on logoff.
  70  */
  71 static uint32_t smbd_audit_sid;
  72 
  73 static void smbd_audit_link(smb_audit_t *);
  74 static smb_audit_t *smbd_audit_unlink(uint32_t);
  75 
  76 
  77 /*
  78  * Invoked at user logon due to SmbSessionSetupX.  Authenticate the
  79  * user.
  80  *
  81  * On error, returns NULL, and status in user_info->lg_status
  82  */
  83 smb_token_t *
  84 smbd_user_auth_logon(smb_logon_t *user_info)
  85 {
  86         smb_token_t *token;
  87         smb_logon_t tmp_user;
  88         char *p;
  89         char *buf = NULL;
  90 
  91         if (user_info->lg_username == NULL ||
  92             user_info->lg_domain == NULL ||
  93             user_info->lg_workstation == NULL) {
  94                 user_info->lg_status = NT_STATUS_INVALID_PARAMETER;
  95                 return (NULL);
  96         }
  97 
  98         /*
  99          * Avoid modifying the caller-provided struct because it
 100          * may or may not point to allocated strings etc.
 101          * Copy to tmp_user, auth, then copy the (out) lg_status
 102          * member back to the caller-provided struct.
 103          */
 104         tmp_user = *user_info;
 105         if (tmp_user.lg_username[0] == '\0') {
 106                 tmp_user.lg_flags |= SMB_ATF_ANON;
 107                 tmp_user.lg_e_username = "anonymous";
 108         } else {
 109                 tmp_user.lg_e_username = tmp_user.lg_username;
 110         }
 111 
 112         /* Handle user@domain format. */
 113         if (tmp_user.lg_domain[0] == '\0' &&
 114             (p = strchr(tmp_user.lg_e_username, '@')) != NULL) {
 115                 buf = strdup(tmp_user.lg_e_username);
 116                 p = buf + (p - tmp_user.lg_e_username);
 117                 *p = '\0';
 118                 tmp_user.lg_e_domain = p + 1;
 119                 tmp_user.lg_e_username = buf;
 120         } else {
 121                 tmp_user.lg_e_domain = tmp_user.lg_domain;
 122         }
 123 
 124         token = smb_logon(&tmp_user);
 125         user_info->lg_status = tmp_user.lg_status;
 126 
 127         if (token == NULL) {
 128                 if (user_info->lg_status == 0) /* should not happen */
 129                         user_info->lg_status = NT_STATUS_INTERNAL_ERROR;
 130         }
 131 
 132         if (!smbd_logon_audit(token, &user_info->lg_clnt_ipaddr,
 133             tmp_user.lg_e_username, tmp_user.lg_e_domain)) {
 134                 user_info->lg_status = NT_STATUS_AUDIT_FAILED;
 135                 goto errout;
 136         }
 137 
 138         if (token) {
 139                 smb_autohome_add(token);
 140         }
 141 
 142         if (buf != NULL)
 143                 free(buf);
 144 
 145         return (token);
 146 
 147 errout:
 148         if (buf != NULL)
 149                 free(buf);
 150         smb_token_destroy(token);
 151         return (NULL);
 152 }
 153 
 154 /* Start an audit session and audit the event. */
 155 boolean_t
 156 smbd_logon_audit(smb_token_t *token, smb_inaddr_t *ipaddr, char *username,
 157     char *domain)
 158 {
 159         smb_audit_t *entry;
 160         adt_session_data_t *ah = NULL;
 161         adt_event_data_t *event;
 162         au_tid_addr_t termid;
 163         char sidbuf[SMB_SID_STRSZ];
 164         uid_t uid;
 165         gid_t gid;
 166         char *sid;
 167         int status;
 168         int retval;
 169 
 170         if (username == NULL)
 171                 username = "";
 172 
 173         if (token == NULL) {
 174                 uid = ADT_NO_ATTRIB;
 175                 gid = ADT_NO_ATTRIB;
 176                 sid = NT_NULL_SIDSTR;
 177                 /* use the 'default' username and domain we were given */
 178                 status = ADT_FAILURE;
 179                 retval = ADT_FAIL_VALUE_AUTH;
 180         } else {
 181                 uid = token->tkn_user.i_id;
 182                 gid = token->tkn_primary_grp.i_id;
 183                 smb_sid_tostr(token->tkn_user.i_sid, sidbuf);
 184                 sid = sidbuf;
 185                 username = token->tkn_account_name;
 186                 domain = token->tkn_domain_name;
 187                 status = ADT_SUCCESS;
 188                 retval = ADT_SUCCESS;
 189         }
 190 
 191         if (adt_start_session(&ah, NULL, 0)) {
 192                 syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m");
 193                 goto errout;
 194         }
 195 
 196         if ((event = adt_alloc_event(ah, ADT_smbd_session)) == NULL) {
 197                 syslog(LOG_AUTH | LOG_ALERT,
 198                     "adt_alloc_event(ADT_smbd_session): %m");
 199                 goto errout;
 200         }
 201 
 202         (void) memset(&termid, 0, sizeof (au_tid_addr_t));
 203         termid.at_port = 445;
 204 
 205         if (ipaddr->a_family == AF_INET) {
 206                 termid.at_addr[0] = ipaddr->a_ipv4;
 207                 termid.at_type = AU_IPv4;
 208         } else {
 209                 bcopy(&ipaddr->a_ip, termid.at_addr,
 210                     sizeof (in6_addr_t));
 211                 termid.at_type = AU_IPv6;
 212         }
 213         adt_set_termid(ah, &termid);
 214 
 215         if (adt_set_user(ah, uid, gid, uid, gid, NULL, ADT_NEW)) {
 216                 syslog(LOG_AUTH | LOG_ALERT, "adt_set_user: %m");
 217                 adt_free_event(event);
 218                 goto errout;
 219         }
 220 
 221         event->adt_smbd_session.domain = domain;
 222         event->adt_smbd_session.username = username;
 223         event->adt_smbd_session.sid = sid;
 224 
 225         if (adt_put_event(event, status, retval))
 226                 syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m");
 227 
 228         adt_free_event(event);
 229 
 230         if (token) {
 231                 if ((entry = malloc(sizeof (smb_audit_t))) == NULL) {
 232                         syslog(LOG_ERR, "smbd_user_auth_logon: %m");
 233                         goto errout;
 234                 }
 235 
 236                 entry->sa_handle = ah;
 237                 entry->sa_uid = uid;
 238                 entry->sa_gid = gid;
 239                 entry->sa_username = strdup(username);
 240                 entry->sa_domain = strdup(domain);
 241 
 242                 smbd_audit_link(entry);
 243                 token->tkn_audit_sid = entry->sa_audit_sid;
 244                 adt_get_auid(ah, &token->tkn_auid);
 245                 adt_get_mask(ah, &token->tkn_amask);
 246                 adt_get_asid(ah, &token->tkn_asid);
 247         }
 248 
 249         return (B_TRUE);
 250 errout:
 251         if (ah != NULL)
 252                 (void) adt_end_session(ah);
 253 
 254         return (B_FALSE);
 255 }
 256 
 257 /*
 258  * Logon due to a subsequent SmbSessionSetupX on an existing session.
 259  * The user was authenticated during the initial session setup.
 260  */
 261 void
 262 smbd_user_nonauth_logon(uint32_t audit_sid)
 263 {
 264         smb_audit_t *entry;
 265 
 266         (void) mutex_lock(&smbd_audit_lock);
 267         entry = smbd_audit_list;
 268 
 269         while (entry) {
 270                 if (entry->sa_audit_sid == audit_sid) {
 271                         ++entry->sa_refcnt;
 272                         break;
 273                 }
 274 
 275                 entry = entry->sa_next;
 276         }
 277 
 278         (void) mutex_unlock(&smbd_audit_lock);
 279 }
 280 
 281 /*
 282  * Invoked at user logoff due to SmbLogoffX.  If this is the final
 283  * logoff for this user on the session, audit the event and terminate
 284  * the audit session.
 285  */
 286 void
 287 smbd_user_auth_logoff(uint32_t audit_sid)
 288 {
 289         smb_audit_t *entry;
 290         adt_session_data_t *ah;
 291         adt_event_data_t *event;
 292         struct passwd pw;
 293         char buf[NSS_LINELEN_PASSWD];
 294 
 295         if ((entry = smbd_audit_unlink(audit_sid)) == NULL)
 296                 return;
 297 
 298         if (IDMAP_ID_IS_EPHEMERAL(entry->sa_uid)) {
 299                 smb_autohome_remove(entry->sa_username);
 300         } else {
 301                 if (getpwuid_r(entry->sa_uid, &pw, buf, sizeof (buf)) == NULL)
 302                         return;
 303 
 304                 smb_autohome_remove(pw.pw_name);
 305         }
 306 
 307         ah = entry->sa_handle;
 308 
 309         if ((event = adt_alloc_event(ah, ADT_smbd_logoff)) == NULL) {
 310                 syslog(LOG_AUTH | LOG_ALERT,
 311                     "adt_alloc_event(ADT_smbd_logoff): %m");
 312         } else {
 313                 event->adt_smbd_logoff.domain = entry->sa_domain;
 314                 event->adt_smbd_logoff.username = entry->sa_username;
 315 
 316                 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS))
 317                         syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m");
 318 
 319                 adt_free_event(event);
 320         }
 321 
 322         (void) adt_end_session(ah);
 323 
 324         free(entry->sa_username);
 325         free(entry->sa_domain);
 326         free(entry);
 327 }
 328 
 329 /*
 330  * Allocate an id and link an audit handle onto the global list.
 331  */
 332 static void
 333 smbd_audit_link(smb_audit_t *entry)
 334 {
 335         (void) mutex_lock(&smbd_audit_lock);
 336 
 337         do {
 338                 ++smbd_audit_sid;
 339         } while ((smbd_audit_sid == 0) || (smbd_audit_sid == (uint32_t)-1));
 340 
 341         entry->sa_audit_sid = smbd_audit_sid;
 342         entry->sa_refcnt = 1;
 343         entry->sa_next = smbd_audit_list;
 344         smbd_audit_list = entry;
 345 
 346         (void) mutex_unlock(&smbd_audit_lock);
 347 }
 348 
 349 /*
 350  * Unlink an audit handle.  If the reference count reaches 0, the entry
 351  * is removed from the list and returned.  Otherwise the entry remains
 352  * on the list and a null pointer is returned.
 353  */
 354 static smb_audit_t *
 355 smbd_audit_unlink(uint32_t audit_sid)
 356 {
 357         smb_audit_t *entry;
 358         smb_audit_t **ppe;
 359 
 360         (void) mutex_lock(&smbd_audit_lock);
 361         ppe = &smbd_audit_list;
 362 
 363         while (*ppe) {
 364                 entry = *ppe;
 365 
 366                 if (entry->sa_audit_sid == audit_sid) {
 367                         if (entry->sa_refcnt == 0)
 368                                 break;
 369 
 370                         if ((--entry->sa_refcnt) != 0)
 371                                 break;
 372 
 373                         *ppe = entry->sa_next;
 374                         (void) mutex_unlock(&smbd_audit_lock);
 375                         return (entry);
 376                 }
 377 
 378                 ppe = &(*ppe)->sa_next;
 379         }
 380 
 381         (void) mutex_unlock(&smbd_audit_lock);
 382         return (NULL);
 383 }