b14885213e91f51f3cebec57c7f6f5385efb5895
[jelmer/samba4-debian.git] / source / winbind / idmap.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Map SIDs to uids/gids and back
5
6    Copyright (C) Kai Blin 2008
7
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.
12
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.
17
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/>.
20 */
21
22 #include "includes.h"
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"
34
35 /**
36  * Get uid/gid bounds from idmap database
37  *
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
42  */
43 static int idmap_get_bounds(struct idmap_context *idmap_ctx, uint32_t *low,
44                 uint32_t *high)
45 {
46         int ret = -1;
47         struct ldb_context *ldb = idmap_ctx->ldb_ctx;
48         struct ldb_dn *dn;
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;
53
54         dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG");
55         if (dn == NULL) goto failed;
56
57         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &res);
58         if (ret != LDB_SUCCESS) goto failed;
59
60         talloc_steal(tmp_ctx, res);
61
62         if (res->count != 1) {
63                 ret = -1;
64                 goto failed;
65         }
66
67         lower_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "lowerBound", -1);
68         if (lower_bound != (uint32_t) -1) {
69                 ret = LDB_SUCCESS;
70         } else {
71                 ret = -1;
72                 goto failed;
73         }
74
75         upper_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "upperBound", -1);
76         if (upper_bound != (uint32_t) -1) {
77                 ret = LDB_SUCCESS;
78         } else {
79                 ret = -1;
80         }
81
82 failed:
83         talloc_free(tmp_ctx);
84         *low  = lower_bound;
85         *high = upper_bound;
86         return ret;
87 }
88
89 /**
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.
97  */
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)
101 {
102         struct ldb_val val;
103         enum ndr_err_code ndr_err;
104
105         ndr_err = ndr_push_struct_blob(&val, mem_ctx,
106                                        lp_iconv_convenience(idmap_ctx->lp_ctx),
107                                        sid,
108                                        (ndr_push_flags_fn_t)ndr_push_dom_sid);
109
110         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
111                 return -1;
112         }
113
114         return ldb_msg_add_value(msg, attr_name, &val, NULL);
115 }
116
117 /**
118  * Get a dom_sid structure from a ldb message.
119  *
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
124  */
125 static struct dom_sid *idmap_msg_get_dom_sid(TALLOC_CTX *mem_ctx,
126                 struct ldb_message *msg, const char *attr_name)
127 {
128         struct dom_sid *sid;
129         const struct ldb_val *val;
130         enum ndr_err_code ndr_err;
131
132         val = ldb_msg_find_ldb_val(msg, attr_name);
133         if (val == NULL) {
134                 return NULL;
135         }
136
137         sid = talloc(mem_ctx, struct dom_sid);
138         if (sid == NULL) {
139                 return NULL;
140         }
141
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)) {
145                 talloc_free(sid);
146                 return NULL;
147         }
148
149         return sid;
150 }
151
152 /**
153  * Initialize idmap context
154  *
155  * talloc_free to close.
156  *
157  * \param mem_ctx talloc context to use.
158  * \return allocated idmap_context on success, NULL on error
159  */
160 struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx,
161                 struct loadparm_context *lp_ctx)
162 {
163         struct idmap_context *idmap_ctx;
164
165         idmap_ctx = talloc(mem_ctx, struct idmap_context);
166         if (idmap_ctx == NULL) {
167                 return NULL;
168         }
169
170         idmap_ctx->lp_ctx = lp_ctx;
171
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),
175                                               NULL, 0, NULL);
176         if (idmap_ctx->ldb_ctx == NULL) {
177                 return NULL;
178         }
179
180         return idmap_ctx;
181 }
182
183 /**
184  * Convert a uid to the corresponding SID
185  *
186  * \param idmap_ctx idmap context to use
187  * \param mem_ctx talloc context the memory for the struct dom_sid is allocated
188  * from.
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
191  * succeeds.
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.
194  */
195
196 NTSTATUS idmap_uid_to_sid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
197                 const uid_t uid, struct dom_sid **sid)
198 {
199         int ret;
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;
204         int trans = -1;
205         uid_t low, high;
206         char *sid_string, *uid_string;
207         struct dom_sid *unix_users_sid, *new_sid;
208         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
209
210         ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
211                                  NULL, "(&(objectClass=sidMap)(uidNumber=%u))",
212                                  uid);
213         if (ret != LDB_SUCCESS) {
214                 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
215                 status = NT_STATUS_NONE_MAPPED;
216                 goto failed;
217         }
218
219         if (res->count == 1) {
220                 *sid = idmap_msg_get_dom_sid(mem_ctx, res->msgs[0],
221                                              "objectSid");
222                 if (*sid == NULL) {
223                         DEBUG(1, ("Failed to get sid from db: %u\n", ret));
224                         status = NT_STATUS_NONE_MAPPED;
225                         goto failed;
226                 }
227                 talloc_free(tmp_ctx);
228                 return NT_STATUS_OK;
229         }
230
231         DEBUG(6, ("uid not found in idmap db, trying to allocate SID.\n"));
232
233         trans = ldb_transaction_start(ldb);
234         if (trans != LDB_SUCCESS) {
235                 status = NT_STATUS_NONE_MAPPED;
236                 goto failed;
237         }
238
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))",
243                                  uid);
244         if (ret != LDB_SUCCESS) {
245                 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
246                 status = NT_STATUS_NONE_MAPPED;
247                 goto failed;
248         }
249
250         if (res->count > 0) {
251                 DEBUG(1, ("sidMap modified while trying to add a mapping.\n"));
252                 status = NT_STATUS_RETRY;
253                 goto failed;
254         }
255
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;
260                 goto failed;
261         }
262
263         if (uid >= low && uid <= high) {
264                 /* An existing user would have been mapped before */
265                 status = NT_STATUS_NO_SUCH_USER;
266                 goto failed;
267         }
268
269         /* For local users, we just create a rid = uid +1, so root doesn't end
270          * up with a 0 rid */
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;
274                 goto failed;
275         }
276
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;
280                 goto failed;
281         }
282
283         sid_string = dom_sid_string(tmp_ctx, new_sid);
284         if (sid_string == NULL) {
285                 status = NT_STATUS_NO_MEMORY;
286                 goto failed;
287         }
288
289         uid_string = talloc_asprintf(tmp_ctx, "%u", uid);
290         if (uid_string == NULL) {
291                 status = NT_STATUS_NO_MEMORY;
292                 goto failed;
293         }
294
295         msg = ldb_msg_new(tmp_ctx);
296         if (msg == NULL) {
297                 status = NT_STATUS_NO_MEMORY;
298                 goto failed;
299         }
300
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;
304                 goto failed;
305         }
306
307         ret = ldb_msg_add_string(msg, "uidNumber", uid_string);
308         if (ret != LDB_SUCCESS) {
309                 status = NT_STATUS_NONE_MAPPED;
310                 goto failed;
311         }
312
313         ret = idmap_msg_add_dom_sid(idmap_ctx, tmp_ctx, msg, "objectSid",
314                                     new_sid);
315         if (ret != LDB_SUCCESS) {
316                 status = NT_STATUS_NONE_MAPPED;
317                 goto failed;
318         }
319
320         ret = ldb_msg_add_string(msg, "objectClass", "sidMap");
321         if (ret != LDB_SUCCESS) {
322                 status = NT_STATUS_NONE_MAPPED;
323                 goto failed;
324         }
325
326         ret = ldb_msg_add_string(msg, "cn", sid_string);
327         if (ret != LDB_SUCCESS) {
328                 status = NT_STATUS_NONE_MAPPED;
329                 goto failed;
330         }
331
332         ret = ldb_add(ldb, msg);
333         if (ret != LDB_SUCCESS) {
334                 status = NT_STATUS_NONE_MAPPED;
335                 goto failed;
336         }
337
338         trans = ldb_transaction_commit(ldb);
339         if (trans != LDB_SUCCESS) {
340                 status = NT_STATUS_NONE_MAPPED;
341                 goto failed;
342         }
343
344         *sid = new_sid;
345         talloc_free(tmp_ctx);
346         return NT_STATUS_OK;
347
348 failed:
349         if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
350         talloc_free(tmp_ctx);
351         return status;
352 }
353
354 /**
355  * Map a Unix gid to the corresponding SID
356  *
357  * \todo Create a SID from the S-1-22-2 range for unmapped groups
358  *
359  * \param idmap_ctx idmap context to use
360  * \param mem_ctx talloc context the memory for the struct dom_sid is allocated
361  * from.
362  * \param gid Unix gid to map to a SID
363  * \param sid Pointer that will take the struct dom_sid pointer if mapping
364  * succeeds.
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.
367  */
368 NTSTATUS idmap_gid_to_sid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
369                 const gid_t gid, struct dom_sid **sid)
370 {
371         return NT_STATUS_NONE_MAPPED;
372 }
373
374 /**
375  * Map a SID to a Unix uid.
376  *
377  * If no mapping exists, a new mapping will be created.
378  *
379  * \todo Create mappings for users not from our primary domain.
380  *
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
387  * mapping failed.
388  */
389 NTSTATUS idmap_sid_to_uid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
390                 const struct dom_sid *sid, uid_t *uid)
391 {
392         return NT_STATUS_NONE_MAPPED;
393 }
394
395 /**
396  * Map a SID to a Unix gid.
397  *
398  * If no mapping exist, a new mapping will be created.
399  *
400  * \todo Create mappings for groups not from our primary domain.
401  *
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
408  * mapping failed.
409  */
410 NTSTATUS idmap_sid_to_gid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
411                 const struct dom_sid *sid, gid_t *gid)
412 {
413         return NT_STATUS_NONE_MAPPED;
414 }
415