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 2014 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, start an audit session and audit the event.
  80  */
  81 smb_token_t *
  82 smbd_user_auth_logon(smb_logon_t *user_info)
  83 {
  84         smb_token_t *token;
  85         smb_audit_t *entry;
  86         adt_session_data_t *ah;
  87         adt_event_data_t *event;
  88         smb_logon_t tmp_user;
  89         au_tid_addr_t termid;
  90         char sidbuf[SMB_SID_STRSZ];
  91         char *username;
  92         char *domain;
  93         uid_t uid;
  94         gid_t gid;
  95         char *sid;
  96         int status;
  97         int retval;
  98 
  99         if (user_info->lg_username == NULL ||
 100             user_info->lg_domain == NULL ||
 101             user_info->lg_workstation == NULL) {
 102                 return (NULL);
 103         }
 104 
 105         tmp_user = *user_info;
 106         if (tmp_user.lg_username[0] == '\0') {
 107                 tmp_user.lg_flags |= SMB_ATF_ANON;
 108                 tmp_user.lg_e_username = "anonymous";
 109         } else {
 110                 tmp_user.lg_e_username = tmp_user.lg_username;
 111         }
 112         tmp_user.lg_e_domain = tmp_user.lg_domain;
 113 
 114         if ((token = smb_logon(&tmp_user)) == NULL) {
 115                 uid = ADT_NO_ATTRIB;
 116                 gid = ADT_NO_ATTRIB;
 117                 sid = NT_NULL_SIDSTR;
 118                 username = tmp_user.lg_e_username;
 119                 domain = tmp_user.lg_e_domain;
 120                 status = ADT_FAILURE;
 121                 retval = ADT_FAIL_VALUE_AUTH;
 122         } else {
 123                 uid = token->tkn_user.i_id;
 124                 gid = token->tkn_primary_grp.i_id;
 125                 smb_sid_tostr(token->tkn_user.i_sid, sidbuf);
 126                 sid = sidbuf;
 127                 username = token->tkn_account_name;
 128                 domain = token->tkn_domain_name;
 129                 status = ADT_SUCCESS;
 130                 retval = ADT_SUCCESS;
 131         }
 132 
 133         if (adt_start_session(&ah, NULL, 0)) {
 134                 syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m");
 135                 smb_token_destroy(token);
 136                 return (NULL);
 137         }
 138 
 139         if ((event = adt_alloc_event(ah, ADT_smbd_session)) == NULL) {
 140                 syslog(LOG_AUTH | LOG_ALERT,
 141                     "adt_alloc_event(ADT_smbd_session): %m");
 142                 (void) adt_end_session(ah);
 143                 smb_token_destroy(token);
 144                 return (NULL);
 145         }
 146 
 147         (void) memset(&termid, 0, sizeof (au_tid_addr_t));
 148         termid.at_port = user_info->lg_local_port;
 149 
 150         if (user_info->lg_clnt_ipaddr.a_family == AF_INET) {
 151                 termid.at_addr[0] = user_info->lg_clnt_ipaddr.a_ipv4;
 152                 termid.at_type = AU_IPv4;
 153         } else {
 154                 bcopy(&user_info->lg_clnt_ipaddr.a_ip, termid.at_addr,
 155                     sizeof (in6_addr_t));
 156                 termid.at_type = AU_IPv6;
 157         }
 158         adt_set_termid(ah, &termid);
 159 
 160         if (adt_set_user(ah, uid, gid, uid, gid, NULL, ADT_NEW)) {
 161                 syslog(LOG_AUTH | LOG_ALERT, "adt_set_user: %m");
 162                 adt_free_event(event);
 163                 (void) adt_end_session(ah);
 164                 smb_token_destroy(token);
 165                 return (NULL);
 166         }
 167 
 168         event->adt_smbd_session.domain = domain;
 169         event->adt_smbd_session.username = username;
 170         event->adt_smbd_session.sid = sid;
 171 
 172         if (adt_put_event(event, status, retval))
 173                 syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m");
 174 
 175         adt_free_event(event);
 176 
 177         if (token) {
 178                 if ((entry = malloc(sizeof (smb_audit_t))) == NULL) {
 179                         syslog(LOG_ERR, "smbd_user_auth_logon: %m");
 180                         (void) adt_end_session(ah);
 181                         smb_token_destroy(token);
 182                         return (NULL);
 183                 }
 184 
 185                 entry->sa_handle = ah;
 186                 entry->sa_uid = uid;
 187                 entry->sa_gid = gid;
 188                 entry->sa_username = strdup(username);
 189                 entry->sa_domain = strdup(domain);
 190 
 191                 smb_autohome_add(token);
 192                 smbd_audit_link(entry);
 193                 token->tkn_audit_sid = entry->sa_audit_sid;
 194         }
 195 
 196         return (token);
 197 }
 198 
 199 /*
 200  * Logon due to a subsequent SmbSessionSetupX on an existing session.
 201  * The user was authenticated during the initial session setup.
 202  */
 203 void
 204 smbd_user_nonauth_logon(uint32_t audit_sid)
 205 {
 206         smb_audit_t *entry;
 207 
 208         (void) mutex_lock(&smbd_audit_lock);
 209         entry = smbd_audit_list;
 210 
 211         while (entry) {
 212                 if (entry->sa_audit_sid == audit_sid) {
 213                         ++entry->sa_refcnt;
 214                         break;
 215                 }
 216 
 217                 entry = entry->sa_next;
 218         }
 219 
 220         (void) mutex_unlock(&smbd_audit_lock);
 221 }
 222 
 223 /*
 224  * Invoked at user logoff due to SmbLogoffX.  If this is the final
 225  * logoff for this user on the session, audit the event and terminate
 226  * the audit session.
 227  */
 228 void
 229 smbd_user_auth_logoff(uint32_t audit_sid)
 230 {
 231         smb_audit_t *entry;
 232         adt_session_data_t *ah;
 233         adt_event_data_t *event;
 234         struct passwd pw;
 235         char buf[NSS_LINELEN_PASSWD];
 236 
 237         if ((entry = smbd_audit_unlink(audit_sid)) == NULL)
 238                 return;
 239 
 240         if (IDMAP_ID_IS_EPHEMERAL(entry->sa_uid)) {
 241                 smb_autohome_remove(entry->sa_username);
 242         } else {
 243                 if (getpwuid_r(entry->sa_uid, &pw, buf, sizeof (buf)) == NULL)
 244                         return;
 245 
 246                 smb_autohome_remove(pw.pw_name);
 247         }
 248 
 249         ah = entry->sa_handle;
 250 
 251         if ((event = adt_alloc_event(ah, ADT_smbd_logoff)) == NULL) {
 252                 syslog(LOG_AUTH | LOG_ALERT,
 253                     "adt_alloc_event(ADT_smbd_logoff): %m");
 254         } else {
 255                 event->adt_smbd_logoff.domain = entry->sa_domain;
 256                 event->adt_smbd_logoff.username = entry->sa_username;
 257 
 258                 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS))
 259                         syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m");
 260 
 261                 adt_free_event(event);
 262         }
 263 
 264         (void) adt_end_session(ah);
 265 
 266         free(entry->sa_username);
 267         free(entry->sa_domain);
 268         free(entry);
 269 }
 270 
 271 /*
 272  * Allocate an id and link an audit handle onto the global list.
 273  */
 274 static void
 275 smbd_audit_link(smb_audit_t *entry)
 276 {
 277         (void) mutex_lock(&smbd_audit_lock);
 278 
 279         do {
 280                 ++smbd_audit_sid;
 281         } while ((smbd_audit_sid == 0) || (smbd_audit_sid == (uint32_t)-1));
 282 
 283         entry->sa_audit_sid = smbd_audit_sid;
 284         entry->sa_refcnt = 1;
 285         entry->sa_next = smbd_audit_list;
 286         smbd_audit_list = entry;
 287 
 288         (void) mutex_unlock(&smbd_audit_lock);
 289 }
 290 
 291 /*
 292  * Unlink an audit handle.  If the reference count reaches 0, the entry
 293  * is removed from the list and returned.  Otherwise the entry remains
 294  * on the list and a null pointer is returned.
 295  */
 296 static smb_audit_t *
 297 smbd_audit_unlink(uint32_t audit_sid)
 298 {
 299         smb_audit_t *entry;
 300         smb_audit_t **ppe;
 301 
 302         (void) mutex_lock(&smbd_audit_lock);
 303         ppe = &smbd_audit_list;
 304 
 305         while (*ppe) {
 306                 entry = *ppe;
 307 
 308                 if (entry->sa_audit_sid == audit_sid) {
 309                         if (entry->sa_refcnt == 0)
 310                                 break;
 311 
 312                         if ((--entry->sa_refcnt) != 0)
 313                                 break;
 314 
 315                         *ppe = entry->sa_next;
 316                         (void) mutex_unlock(&smbd_audit_lock);
 317                         return (entry);
 318                 }
 319 
 320                 ppe = &(*ppe)->sa_next;
 321         }
 322 
 323         (void) mutex_unlock(&smbd_audit_lock);
 324         return (NULL);
 325 }