idmap: Handle gid->SID mapping
[sfrench/samba-autobuild/.git] / source4 / 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  * \param idmap_ctx idmap context to use
358  * \param mem_ctx talloc context the memory for the struct dom_sid is allocated
359  * from.
360  * \param gid Unix gid to map to a SID
361  * \param sid Pointer that will take the struct dom_sid pointer if mapping
362  * succeeds.
363  * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping not
364  * possible or some other NTSTATUS that is more descriptive on failure.
365  */
366 NTSTATUS idmap_gid_to_sid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
367                 const gid_t gid, struct dom_sid **sid)
368 {
369         int ret;
370         NTSTATUS status = NT_STATUS_NONE_MAPPED;
371         struct ldb_context *ldb = idmap_ctx->ldb_ctx;
372         struct ldb_message *msg;
373         struct ldb_result *res = NULL;
374         int trans = -1;
375         gid_t low, high;
376         char *sid_string, *gid_string;
377         struct dom_sid *unix_groups_sid, *new_sid;
378         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
379
380         ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
381                         NULL, "(&(objectClass=sidMap)(gidNumber=%u))", gid);
382         if (ret != LDB_SUCCESS) {
383                 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
384                 status = NT_STATUS_NONE_MAPPED;
385                 goto failed;
386         }
387
388         if (res->count == 1) {
389                 *sid = idmap_msg_get_dom_sid(mem_ctx, res->msgs[0],
390                                 "objectSid");
391                 if (*sid == NULL) {
392                         DEBUG(1, ("Failed to get sid from db: %u\n", ret));
393                         status = NT_STATUS_NONE_MAPPED;
394                         goto failed;
395                 }
396                 /* No change, so cancel the transaction */
397                 ldb_transaction_cancel(ldb);
398                 talloc_free(tmp_ctx);
399                 return NT_STATUS_OK;
400         }
401
402         DEBUG(6, ("gid not found in idmap db, trying to allocate SID.\n"));
403
404         trans = ldb_transaction_start(ldb);
405         if (trans != LDB_SUCCESS) {
406                 status = NT_STATUS_NONE_MAPPED;
407                 goto failed;
408         }
409
410         /* Now redo the search to make sure noone added a mapping for that SID
411          * while we weren't looking.*/
412         ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
413                                  NULL, "(&(objectClass=sidMap)(gidNumber=%u))",
414                                  gid);
415         if (ret != LDB_SUCCESS) {
416                 DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
417                 status = NT_STATUS_NONE_MAPPED;
418                 goto failed;
419         }
420
421         if (res->count > 0) {
422                 DEBUG(1, ("sidMap modified while trying to add a mapping.\n"));
423                 status = NT_STATUS_RETRY;
424                 goto failed;
425         }
426
427         ret = idmap_get_bounds(idmap_ctx, &low, &high);
428         if (ret != LDB_SUCCESS) {
429                 DEBUG(1, ("Failed to get id bounds from db: %u\n", ret));
430                 status = NT_STATUS_NONE_MAPPED;
431                 goto failed;
432         }
433
434         if (gid >= low && gid <= high) {
435                 /* An existing group would have been mapped before */
436                 status = NT_STATUS_NO_SUCH_USER;
437                 goto failed;
438         }
439
440         /* For local groups, we just create a rid = gid +1, so root doesn't end
441          * up with a 0 rid */
442         unix_groups_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-22-2");
443         if (unix_groups_sid == NULL) {
444                 status = NT_STATUS_NO_MEMORY;
445                 goto failed;
446         }
447
448         new_sid = dom_sid_add_rid(mem_ctx, unix_groups_sid, gid + 1);
449         if (new_sid == NULL) {
450                 status = NT_STATUS_NO_MEMORY;
451                 goto failed;
452         }
453
454         sid_string = dom_sid_string(tmp_ctx, new_sid);
455         if (sid_string == NULL) {
456                 status = NT_STATUS_NO_MEMORY;
457                 goto failed;
458         }
459
460         gid_string = talloc_asprintf(tmp_ctx, "%u", gid);
461         if (gid_string == NULL) {
462                 status = NT_STATUS_NO_MEMORY;
463                 goto failed;
464         }
465
466         msg = ldb_msg_new(tmp_ctx);
467         if (msg == NULL) {
468                 status = NT_STATUS_NO_MEMORY;
469                 goto failed;
470         }
471
472         msg->dn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s", sid_string);
473         if (msg->dn == NULL) {
474                 status = NT_STATUS_NO_MEMORY;
475                 goto failed;
476         }
477
478         ret = ldb_msg_add_string(msg, "gidNumber", gid_string);
479         if (ret != LDB_SUCCESS) {
480                 status = NT_STATUS_NONE_MAPPED;
481                 goto failed;
482         }
483
484         ret = idmap_msg_add_dom_sid(idmap_ctx, tmp_ctx, msg, "objectSid",
485                         new_sid);
486         if (ret != LDB_SUCCESS) {
487                 status = NT_STATUS_NONE_MAPPED;
488                 goto failed;
489         }
490
491         ret = ldb_msg_add_string(msg, "objectClass", "sidMap");
492         if (ret != LDB_SUCCESS) {
493                 status = NT_STATUS_NONE_MAPPED;
494                 goto failed;
495         }
496
497         ret = ldb_msg_add_string(msg, "cn", sid_string);
498         if (ret != LDB_SUCCESS) {
499                 status = NT_STATUS_NONE_MAPPED;
500                 goto failed;
501         }
502
503         ret = ldb_add(ldb, msg);
504         if (ret != LDB_SUCCESS) {
505                 status = NT_STATUS_NONE_MAPPED;
506                 goto failed;
507         }
508
509         trans = ldb_transaction_commit(ldb);
510         if (trans != LDB_SUCCESS) {
511                 status = NT_STATUS_NONE_MAPPED;
512                 goto failed;
513         }
514
515         *sid = new_sid;
516         talloc_free(tmp_ctx);
517         return NT_STATUS_OK;
518
519 failed:
520         if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
521         talloc_free(tmp_ctx);
522         return status;
523 }
524
525 /**
526  * Map a SID to a Unix uid.
527  *
528  * If no mapping exists, a new mapping will be created.
529  *
530  * \todo Create mappings for users not from our primary domain.
531  *
532  * \param idmap_ctx idmap context to use
533  * \param mem_ctx talloc context to use
534  * \param sid SID to map to a Unix uid
535  * \param uid pointer to receive the mapped uid
536  * \return NT_STATUS_OK on success, NT_STATUS_INVALID_SID if the sid is not from
537  * a trusted domain and idmap trusted only = true, NT_STATUS_NONE_MAPPED if the
538  * mapping failed.
539  */
540 NTSTATUS idmap_sid_to_uid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
541                 const struct dom_sid *sid, uid_t *uid)
542 {
543         return NT_STATUS_NONE_MAPPED;
544 }
545
546 /**
547  * Map a SID to a Unix gid.
548  *
549  * If no mapping exist, a new mapping will be created.
550  *
551  * \todo Create mappings for groups not from our primary domain.
552  *
553  * \param idmap_ctx idmap context to use
554  * \param mem_ctx talloc context to use
555  * \param sid SID to map to a Unix gid
556  * \param gid pointer to receive the mapped gid
557  * \return NT_STATUS_OK on success, NT_STATUS_INVALID_SID if the sid is not from
558  * a trusted domain and idmap trusted only = true, NT_STATUS_NONE_MAPPED if the
559  * mapping failed.
560  */
561 NTSTATUS idmap_sid_to_gid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
562                 const struct dom_sid *sid, gid_t *gid)
563 {
564         return NT_STATUS_NONE_MAPPED;
565 }
566