2 Unix SMB/CIFS implementation.
4 Map SIDs to uids/gids and back
6 Copyright (C) Kai Blin 2008
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "auth/auth.h"
24 #include "librpc/gen_ndr/lsa.h"
25 #include "librpc/gen_ndr/samr.h"
26 #include "librpc/gen_ndr/ndr_security.h"
27 #include "lib/ldb/include/ldb.h"
28 #include "lib/ldb/include/ldb_errors.h"
29 #include "lib/ldb_wrap.h"
30 #include "param/param.h"
31 #include "winbind/idmap.h"
32 #include "libcli/security/proto.h"
33 #include "libcli/ldap/ldap_ndr.h"
36 * Get uid/gid bounds from idmap database
38 * \param idmap_ctx idmap context to use
39 * \param low lower uid/gid bound is stored here
40 * \param high upper uid/gid bound is stored here
41 * \return 0 on success, nonzero on failure
43 static int idmap_get_bounds(struct idmap_context *idmap_ctx, uint32_t *low,
47 struct ldb_context *ldb = idmap_ctx->ldb_ctx;
49 struct ldb_result *res = NULL;
50 TALLOC_CTX *tmp_ctx = talloc_new(idmap_ctx);
51 uint32_t lower_bound = (uint32_t) -1;
52 uint32_t upper_bound = (uint32_t) -1;
54 dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG");
55 if (dn == NULL) goto failed;
57 ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &res);
58 if (ret != LDB_SUCCESS) goto failed;
60 talloc_steal(tmp_ctx, res);
62 if (res->count != 1) {
67 lower_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "lowerBound", -1);
68 if (lower_bound != (uint32_t) -1) {
75 upper_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "upperBound", -1);
76 if (upper_bound != (uint32_t) -1) {
90 * Add a dom_sid structure to a ldb_message
91 * \param idmap_ctx idmap context to use
92 * \param mem_ctx talloc context to use
93 * \param ldb_message ldb message to add dom_sid to
94 * \param attr_name name of the attribute to store the dom_sid in
95 * \param sid dom_sid to store
96 * \return 0 on success, an ldb error code on failure.
98 static int idmap_msg_add_dom_sid(struct idmap_context *idmap_ctx,
99 TALLOC_CTX *mem_ctx, struct ldb_message *msg,
100 const char *attr_name, const struct dom_sid *sid)
103 enum ndr_err_code ndr_err;
105 ndr_err = ndr_push_struct_blob(&val, mem_ctx,
106 lp_iconv_convenience(idmap_ctx->lp_ctx),
108 (ndr_push_flags_fn_t)ndr_push_dom_sid);
110 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
114 return ldb_msg_add_value(msg, attr_name, &val, NULL);
118 * Get a dom_sid structure from a ldb message.
120 * \param mem_ctx talloc context to allocate dom_sid memory in
121 * \param msg ldb_message to get dom_sid from
122 * \param attr_name key that has the dom_sid as data
123 * \return dom_sid structure on success, NULL on failure
125 static struct dom_sid *idmap_msg_get_dom_sid(TALLOC_CTX *mem_ctx,
126 struct ldb_message *msg, const char *attr_name)
129 const struct ldb_val *val;
130 enum ndr_err_code ndr_err;
132 val = ldb_msg_find_ldb_val(msg, attr_name);
137 sid = talloc(mem_ctx, struct dom_sid);
142 ndr_err = ndr_pull_struct_blob(val, sid, NULL, sid,
143 (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
144 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
153 * Initialize idmap context
155 * talloc_free to close.
157 * \param mem_ctx talloc context to use.
158 * \return allocated idmap_context on success, NULL on error
160 struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx,
161 struct loadparm_context *lp_ctx)
163 struct idmap_context *idmap_ctx;
165 idmap_ctx = talloc(mem_ctx, struct idmap_context);
166 if (idmap_ctx == NULL) {
170 idmap_ctx->lp_ctx = lp_ctx;
172 idmap_ctx->ldb_ctx = ldb_wrap_connect(mem_ctx, lp_ctx,
173 lp_idmap_url(lp_ctx),
174 system_session(mem_ctx, lp_ctx),
176 if (idmap_ctx->ldb_ctx == NULL) {
184 * Convert a uid to the corresponding SID
186 * \param idmap_ctx idmap context to use
187 * \param mem_ctx talloc context the memory for the struct dom_sid is allocated
189 * \param uid Unix uid to map to a SID
190 * \param sid Pointer that will take the struct dom_sid pointer if the mapping
192 * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping not
193 * possible or some other NTSTATUS that is more descriptive on failure.
196 NTSTATUS idmap_uid_to_sid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
197 const uid_t uid, struct dom_sid **sid)
200 NTSTATUS status = NT_STATUS_NONE_MAPPED;
201 struct ldb_context *ldb = idmap_ctx->ldb_ctx;
202 struct ldb_message *msg;
203 struct ldb_result *res = NULL;
206 char *sid_string, *uid_string;
207 struct dom_sid *unix_users_sid, *new_sid;
208 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
210 ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
211 NULL, "(&(objectClass=sidMap)(uidNumber=%u))",
213 if (ret != LDB_SUCCESS) {
214 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
215 status = NT_STATUS_NONE_MAPPED;
219 if (res->count == 1) {
220 *sid = idmap_msg_get_dom_sid(mem_ctx, res->msgs[0],
223 DEBUG(1, ("Failed to get sid from db: %u\n", ret));
224 status = NT_STATUS_NONE_MAPPED;
227 talloc_free(tmp_ctx);
231 DEBUG(6, ("uid not found in idmap db, trying to allocate SID.\n"));
233 trans = ldb_transaction_start(ldb);
234 if (trans != LDB_SUCCESS) {
235 status = NT_STATUS_NONE_MAPPED;
239 /* Now redo the search to make sure noone added a mapping for that SID
240 * while we weren't looking.*/
241 ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
242 NULL, "(&(objectClass=sidMap)(uidNumber=%u))",
244 if (ret != LDB_SUCCESS) {
245 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
246 status = NT_STATUS_NONE_MAPPED;
250 if (res->count > 0) {
251 DEBUG(1, ("sidMap modified while trying to add a mapping.\n"));
252 status = NT_STATUS_RETRY;
256 ret = idmap_get_bounds(idmap_ctx, &low, &high);
257 if (ret != LDB_SUCCESS) {
258 DEBUG(1, ("Failed to get id bounds from db: %u\n", ret));
259 status = NT_STATUS_NONE_MAPPED;
263 if (uid >= low && uid <= high) {
264 /* An existing user would have been mapped before */
265 status = NT_STATUS_NO_SUCH_USER;
269 /* For local users, we just create a rid = uid +1, so root doesn't end
271 unix_users_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-22-1");
272 if (unix_users_sid == NULL) {
273 status = NT_STATUS_NO_MEMORY;
277 new_sid = dom_sid_add_rid(mem_ctx, unix_users_sid, uid + 1);
278 if (new_sid == NULL) {
279 status = NT_STATUS_NO_MEMORY;
283 sid_string = dom_sid_string(tmp_ctx, new_sid);
284 if (sid_string == NULL) {
285 status = NT_STATUS_NO_MEMORY;
289 uid_string = talloc_asprintf(tmp_ctx, "%u", uid);
290 if (uid_string == NULL) {
291 status = NT_STATUS_NO_MEMORY;
295 msg = ldb_msg_new(tmp_ctx);
297 status = NT_STATUS_NO_MEMORY;
301 msg->dn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s", sid_string);
302 if (msg->dn == NULL) {
303 status = NT_STATUS_NO_MEMORY;
307 ret = ldb_msg_add_string(msg, "uidNumber", uid_string);
308 if (ret != LDB_SUCCESS) {
309 status = NT_STATUS_NONE_MAPPED;
313 ret = idmap_msg_add_dom_sid(idmap_ctx, tmp_ctx, msg, "objectSid",
315 if (ret != LDB_SUCCESS) {
316 status = NT_STATUS_NONE_MAPPED;
320 ret = ldb_msg_add_string(msg, "objectClass", "sidMap");
321 if (ret != LDB_SUCCESS) {
322 status = NT_STATUS_NONE_MAPPED;
326 ret = ldb_msg_add_string(msg, "cn", sid_string);
327 if (ret != LDB_SUCCESS) {
328 status = NT_STATUS_NONE_MAPPED;
332 ret = ldb_add(ldb, msg);
333 if (ret != LDB_SUCCESS) {
334 status = NT_STATUS_NONE_MAPPED;
338 trans = ldb_transaction_commit(ldb);
339 if (trans != LDB_SUCCESS) {
340 status = NT_STATUS_NONE_MAPPED;
345 talloc_free(tmp_ctx);
349 if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
350 talloc_free(tmp_ctx);
355 * Map a Unix gid to the corresponding SID
357 * \todo Create a SID from the S-1-22-2 range for unmapped groups
359 * \param idmap_ctx idmap context to use
360 * \param mem_ctx talloc context the memory for the struct dom_sid is allocated
362 * \param gid Unix gid to map to a SID
363 * \param sid Pointer that will take the struct dom_sid pointer if mapping
365 * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping not
366 * possible or some other NTSTATUS that is more descriptive on failure.
368 NTSTATUS idmap_gid_to_sid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
369 const gid_t gid, struct dom_sid **sid)
371 return NT_STATUS_NONE_MAPPED;
375 * Map a SID to a Unix uid.
377 * If no mapping exists, a new mapping will be created.
379 * \todo Create mappings for users not from our primary domain.
381 * \param idmap_ctx idmap context to use
382 * \param mem_ctx talloc context to use
383 * \param sid SID to map to a Unix uid
384 * \param uid pointer to receive the mapped uid
385 * \return NT_STATUS_OK on success, NT_STATUS_INVALID_SID if the sid is not from
386 * a trusted domain and idmap trusted only = true, NT_STATUS_NONE_MAPPED if the
389 NTSTATUS idmap_sid_to_uid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
390 const struct dom_sid *sid, uid_t *uid)
392 return NT_STATUS_NONE_MAPPED;
396 * Map a SID to a Unix gid.
398 * If no mapping exist, a new mapping will be created.
400 * \todo Create mappings for groups not from our primary domain.
402 * \param idmap_ctx idmap context to use
403 * \param mem_ctx talloc context to use
404 * \param sid SID to map to a Unix gid
405 * \param gid pointer to receive the mapped gid
406 * \return NT_STATUS_OK on success, NT_STATUS_INVALID_SID if the sid is not from
407 * a trusted domain and idmap trusted only = true, NT_STATUS_NONE_MAPPED if the
410 NTSTATUS idmap_sid_to_gid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
411 const struct dom_sid *sid, gid_t *gid)
413 return NT_STATUS_NONE_MAPPED;