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 /*
27 * Authentication helpers for building credentials
28 */
29
30 #include <sys/types.h>
31 #include <sys/sid.h>
32 #include <sys/priv_names.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
35 #include <netinet/in.h>
36 #include <smbsrv/smb_idmap.h>
37 #include <smbsrv/smb_kproto.h>
38 #include <smbsrv/smb_token.h>
39
40 static void smb_cred_set_sid(smb_id_t *id, ksid_t *ksid);
41 static ksidlist_t *smb_cred_set_sidlist(smb_ids_t *token_grps);
42
43 /*
44 * Allocate a Solaris cred and initialize it based on the access token.
45 *
46 * If the user can be mapped to a non-ephemeral ID, the cred gid is set
47 * to the Solaris user's primary group.
48 *
49 * If the mapped UID is ephemeral, or the primary group could not be
50 * obtained, the cred gid is set to whatever Solaris group is mapped
51 * to the token's primary group.
52 *
53 * Also add any privileges that should always be in effect for this user.
54 * Note that an SMB user object also gets a u_privcred which is used
55 * when the client opens an object with "backup/restore intent".
56 * That cred is setup later, in smb_user_setcred().
57 */
58 cred_t *
59 smb_cred_create(smb_token_t *token, smb_session_t *s)
60 {
61 ksid_t ksid;
62 ksidlist_t *ksidlist = NULL;
63 smb_posix_grps_t *posix_grps;
64 cred_t *cr;
65 gid_t gid;
66 auditinfo_addr_t *au;
67
68 ASSERT(token);
69 ASSERT(token->tkn_posix_grps);
70 posix_grps = token->tkn_posix_grps;
71
72 cr = crget();
73 ASSERT(cr != NULL);
74
75 if (!IDMAP_ID_IS_EPHEMERAL(token->tkn_user.i_id) &&
76 (posix_grps->pg_ngrps != 0)) {
77 gid = posix_grps->pg_grps[0];
78 } else {
79 gid = token->tkn_primary_grp.i_id;
80 }
81
82 if (crsetugid(cr, token->tkn_user.i_id, gid) != 0) {
83 crfree(cr);
84 return (NULL);
85 }
86
87 if (crsetgroups(cr, posix_grps->pg_ngrps, posix_grps->pg_grps) != 0) {
88 crfree(cr);
89 return (NULL);
90 }
91
92 smb_cred_set_sid(&token->tkn_user, &ksid);
93 crsetsid(cr, &ksid, KSID_USER);
94 smb_cred_set_sid(&token->tkn_primary_grp, &ksid);
95 crsetsid(cr, &ksid, KSID_GROUP);
96 smb_cred_set_sid(&token->tkn_owner, &ksid);
97 crsetsid(cr, &ksid, KSID_OWNER);
98 ksidlist = smb_cred_set_sidlist(&token->tkn_win_grps);
99 crsetsidlist(cr, ksidlist);
100
101 /*
102 * In the AD world, "take ownership privilege" is very much
103 * like having Unix "root" privileges. It's normally given
104 * to members of the "Administrators" group, which normally
105 * includes the the local Administrator (like root) and when
106 * joined to a domain, "Domain Admins".
107 */
108 if (smb_token_query_privilege(token, SE_TAKE_OWNERSHIP_LUID)) {
109 (void) crsetpriv(cr,
110 PRIV_FILE_CHOWN,
111 PRIV_FILE_DAC_READ,
112 PRIV_FILE_DAC_SEARCH,
113 PRIV_FILE_DAC_WRITE,
114 PRIV_FILE_OWNER,
115 NULL);
116 }
117
118 /*
119 * See smb.4 bypass_traverse_checking
120 *
121 * For historical reasons, the Windows privilege is named
122 * SeChangeNotifyPrivilege, though the description is
123 * "Bypass traverse checking".
124 */
125 if (smb_token_query_privilege(token, SE_CHANGE_NOTIFY_LUID)) {
126 (void) crsetpriv(cr, PRIV_FILE_DAC_SEARCH, NULL);
127 }
128
129 au = crgetauinfo_modifiable(cr);
130 if (au != NULL) {
131 au->ai_auid = token->tkn_auid;
132 au->ai_mask = token->tkn_amask;
133 au->ai_asid = token->tkn_asid;
134 au->ai_termid.at_port = s->s_local_port;
135
136 if (s->ipaddr.a_family == AF_INET) {
137 au->ai_termid.at_addr[0] = s->ipaddr.a_ipv4;
138 au->ai_termid.at_type = AU_IPv4;
139 } else {
140 bcopy(&s->ipaddr.a_ip, au->ai_termid.at_addr,
141 sizeof (in6_addr_t));
142 au->ai_termid.at_type = AU_IPv6;
143 }
144 }
145 return (cr);
146 }
147
148 /*
149 * Initialize the ksid based on the given smb_id_t.
150 */
151 static void
152 smb_cred_set_sid(smb_id_t *id, ksid_t *ksid)
153 {
154 char sidstr[SMB_SID_STRSZ];
155 int rc;
156
157 ASSERT(id);
158 ASSERT(id->i_sid);
159
160 ksid->ks_id = id->i_id;
161 smb_sid_tostr(id->i_sid, sidstr);
162 rc = smb_sid_splitstr(sidstr, &ksid->ks_rid);
163 ASSERT(rc == 0);
164
165 ksid->ks_attr = id->i_attrs;
166 ksid->ks_domain = ksid_lookupdomain(sidstr);
167 }
168
169 /*
170 * Allocate and initialize the ksidlist based on the access token group list.
171 */
172 static ksidlist_t *
173 smb_cred_set_sidlist(smb_ids_t *token_grps)
174 {
175 int i;
176 ksidlist_t *lp;
177
178 lp = kmem_zalloc(KSIDLIST_MEM(token_grps->i_cnt), KM_SLEEP);
179 lp->ksl_ref = 1;
180 lp->ksl_nsid = token_grps->i_cnt;
181 lp->ksl_neid = 0;
182
183 for (i = 0; i < lp->ksl_nsid; i++) {
184 smb_cred_set_sid(&token_grps->i_ids[i], &lp->ksl_sids[i]);
185 if (lp->ksl_sids[i].ks_id > IDMAP_WK__MAX_GID)
186 lp->ksl_neid++;
187 }
188
189 return (lp);
190 }
191
192 /*
193 * Special variant of smb_cred_create() used when we need an
194 * SMB kcred (i.e. DH import). The returned cred must be
195 * from crget() so it can be passed to smb_user_setcred().
196 */
197 cred_t *
198 smb_kcred_create(void)
199 {
200 cred_t *cr;
201
202 cr = crget();
203 ASSERT(cr != NULL);
204
205 return (cr);
206 }