Some progress towards gums and tdbsam2
[samba.git] / source3 / sam / gumm_tdb.c
1 /*
2  * Unix SMB/CIFS implementation. 
3  * SMB parameters and setup
4  * Copyright (C) Andrew Tridgell 1992-1998
5  * Copyright (C) Simo Sorce 2000-2002
6  * Copyright (C) Gerald Carter 2000
7  * Copyright (C) Jeremy Allison 2001
8  * Copyright (C) Andrew Bartlett 2002
9  * 
10  * This program is free software; you can redistribute it and/or modify it under
11  * the terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 2 of the License, or (at your option)
13  * any later version.
14  * 
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
18  * more details.
19  * 
20  * You should have received a copy of the GNU General Public License along with
21  * this program; if not, write to the Free Software Foundation, Inc., 675
22  * Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 #include "includes.h"
26 #include "tdbsam2.h"
27 #include "tdbsam2_parse_info.h"
28
29 static int tdbgumm_debug_level = DBGC_ALL;
30 #undef DBGC_CLASS
31 #define DBGC_CLASS tdbgumm_debug_level
32
33 #define TDBSAM_VERSION          20021215
34 #define TDB_FILE_NAME           "tdbsam2.tdb"
35 #define NAMEPREFIX              "NAME_"
36 #define SIDPREFIX               "SID_"
37 #define PRIVILEGEPREFIX         "PRIV_"
38
39 #define TDB_FORMAT_STRING       "ddB"
40
41 #define TALLOC_CHECK(ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: Out of memory!\n", __FUNCTION__)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
42 #define SET_OR_FAIL(func, label) do { if (NT_STATUS_IS_ERR(func)) { DEBUG(0, ("%s: Setting gums object data failed!\n", __FUNCTION__)); goto label; } } while(0)
43
44 struct tdbsam2_enum_objs {
45         uint32 type;
46         fstring dom_sid;
47         TDB_CONTEXT *db;
48         TDB_DATA key;
49         struct tdbsam2_enum_objs *next;
50 };
51
52 union tdbsam2_data {
53         struct tdbsam2_domain_data *domain;
54         struct tdbsam2_user_data *user;
55         struct tdbsam2_group_data *group;
56 };
57
58 struct tdbsam2_object {
59         uint32 type;
60         uint32 version;
61         union tdbsam2_data data;
62 };
63
64 static TDB_CONTEXT *tdbsam2_db;
65
66 struct tdbsam2_enum_objs **teo_handlers;
67
68 static NTSTATUS init_tdbsam2_object_from_buffer(struct tdbsam2_object *object, TALLOC_CTX *mem_ctx, char *buffer, int size)
69 {
70
71         NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
72         int iret;
73         char *obj_data;
74         int data_size = 0;
75         int len;
76         
77         len = tdb_unpack (buffer, size, TDB_FORMAT_STRING,
78                           &(object->version),
79                           &(object->type),
80                           &data_size, &obj_data);
81
82         if (len == -1)
83                 goto done;
84
85         /* version is checked inside this function so that backward compatibility code can be
86            called eventually.
87            this way we can easily handle database format upgrades */
88         if (object->version != TDBSAM_VERSION) {
89                 DEBUG(3,("init_tdbsam2_object_from_buffer: Error, db object has wrong tdbsam version!\n"));
90                 goto done;
91         }
92
93         /* be sure the string is terminated before trying to parse it */
94         if (obj_data[data_size - 1] != '\0')
95                 obj_data[data_size - 1] = '\0';
96
97         switch (object->type) {
98                 case GUMS_OBJ_DOMAIN:
99                         object->data.domain = (struct tdbsam2_domain_data *)talloc(mem_ctx, sizeof(struct tdbsam2_domain_data));
100                         TALLOC_CHECK(object->data.domain, ret, done);
101                         memset(object->data.domain, 0, sizeof(struct tdbsam2_domain_data));
102
103                         iret = gen_parse(mem_ctx, pinfo_tdbsam2_domain_data, (char *)(object->data.domain), obj_data);
104                         break;
105                 case GUMS_OBJ_GROUP:
106                 case GUMS_OBJ_ALIAS:
107                         object->data.group = (struct tdbsam2_group_data *)talloc(mem_ctx, sizeof(struct tdbsam2_group_data));
108                         TALLOC_CHECK(object->data.group, ret, done);
109                         memset(object->data.group, 0, sizeof(struct tdbsam2_group_data));
110
111                         iret = gen_parse(mem_ctx, pinfo_tdbsam2_group_data, (char *)(object->data.group), obj_data);
112                         break;
113                 case GUMS_OBJ_NORMAL_USER:
114                         object->data.user = (struct tdbsam2_user_data *)talloc(mem_ctx, sizeof(struct tdbsam2_user_data));
115                         TALLOC_CHECK(object->data.user, ret, done);
116                         memset(object->data.user, 0, sizeof(struct tdbsam2_user_data));
117
118                         iret = gen_parse(mem_ctx, pinfo_tdbsam2_user_data, (char *)(object->data.user), obj_data);
119                         break;
120                 default:
121                         DEBUG(3,("init_tdbsam2_object_from_buffer: Error, wrong object type number!\n"));
122                         goto done;
123         }
124
125         if (iret != 0) {
126                 DEBUG(0,("init_tdbsam2_object_from_buffer: Fatal Error! Unable to parse object!\n"));
127                 DEBUG(0,("init_tdbsam2_object_from_buffer: DB Corrupted ?"));
128                 goto done;
129         }
130
131         ret = NT_STATUS_OK;
132 done:
133         SAFE_FREE(obj_data);
134         return ret;
135 }
136
137 static NTSTATUS init_buffer_from_tdbsam2_object(char **buffer, size_t *len, TALLOC_CTX *mem_ctx, struct tdbsam2_object *object)
138 {
139
140         NTSTATUS ret;
141         char *buf1 = NULL;
142         size_t buflen;
143
144         if (!buffer)
145                 return NT_STATUS_INVALID_PARAMETER;
146
147         switch (object->type) {
148                 case GUMS_OBJ_DOMAIN:
149                         buf1 = gen_dump(mem_ctx, pinfo_tdbsam2_domain_data, (char *)(object->data.domain), 0);
150                         break;
151                 case GUMS_OBJ_GROUP:
152                 case GUMS_OBJ_ALIAS:
153                         buf1 = gen_dump(mem_ctx, pinfo_tdbsam2_group_data, (char *)(object->data.group), 0);
154                         break;
155                 case GUMS_OBJ_NORMAL_USER:
156                         buf1 = gen_dump(mem_ctx, pinfo_tdbsam2_user_data, (char *)(object->data.user), 0);
157                         break;
158                 default:
159                         DEBUG(3,("init_buffer_from_tdbsam2_object: Error, wrong object type number!\n"));
160                         return NT_STATUS_UNSUCCESSFUL;  
161         }
162         
163         if (buf1 == NULL) {
164                 DEBUG(0, ("init_buffer_from_tdbsam2_object: Fatal Error! Unable to dump object!\n"));
165                 return NT_STATUS_UNSUCCESSFUL;
166         }
167
168         buflen = tdb_pack(NULL, 0,  TDB_FORMAT_STRING,
169                         TDBSAM_VERSION,
170                         object->type,
171                         strlen(buf1) + 1, buf1);
172
173         *buffer = talloc(mem_ctx, buflen);
174         TALLOC_CHECK(*buffer, ret, done);
175
176         *len = tdb_pack(*buffer, buflen, TDB_FORMAT_STRING,
177                         TDBSAM_VERSION,
178                         object->type,
179                         strlen(buf1) + 1, buf1);
180
181         if (*len != buflen) {
182                 DEBUG(0, ("init_tdb_data_from_tdbsam2_object: somthing odd is going on here: bufflen (%d) != len (%d) in tdb_pack operations!\n", 
183                           buflen, *len));
184                 *buffer = NULL;
185                 ret = NT_STATUS_UNSUCCESSFUL;
186                 goto done;
187         }
188
189         ret = NT_STATUS_OK;
190 done:
191         return ret;
192 }
193
194 static NTSTATUS opentdb(void)
195 {
196         if (!tdbsam2_db) {
197                 pstring tdbfile;
198                 get_private_directory(tdbfile);
199                 pstrcat(tdbfile, "/");
200                 pstrcat(tdbfile, TDB_FILE_NAME);
201
202                 tdbsam2_db = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0600);
203                 if (!tdbsam2_db)
204                 {
205                         DEBUG(0, ("opentdb: Unable to open database (%s)!\n", tdbfile));
206                         return NT_STATUS_UNSUCCESSFUL;
207                 }
208         }
209
210         return NT_STATUS_OK;
211 }
212
213 static NTSTATUS get_object_by_sid(TALLOC_CTX *mem_ctx, struct tdbsam2_object *obj, const DOM_SID *sid)
214 {
215         NTSTATUS ret;
216         TDB_DATA data, key;
217         fstring keystr;
218
219         if (!obj || !mem_ctx || !sid)
220                 return NT_STATUS_INVALID_PARAMETER;
221
222         if (NT_STATUS_IS_ERR(ret = opentdb())) {
223                 return ret;
224         }
225
226         slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sid_string_static(sid));
227         key.dptr = keystr;
228         key.dsize = strlen(keystr) + 1;
229
230         data = tdb_fetch(tdbsam2_db, key);
231         if (!data.dptr) {
232                 DEBUG(5, ("get_object_by_sid: Error fetching database, domain entry not found!\n"));
233                 DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
234                 DEBUGADD(5, (" Key: %s\n", keystr));
235                 return NT_STATUS_UNSUCCESSFUL;
236         }
237
238         if (NT_STATUS_IS_ERR(init_tdbsam2_object_from_buffer(obj, mem_ctx, data.dptr, data.dsize))) {
239                 SAFE_FREE(data.dptr);
240                 DEBUG(0, ("get_object_by_sid: Error fetching database, malformed entry!\n"));
241                 return NT_STATUS_UNSUCCESSFUL;
242         }
243         SAFE_FREE(data.dptr);
244
245         return NT_STATUS_OK;
246         
247 }
248
249 static NTSTATUS get_object_by_name(TALLOC_CTX *mem_ctx, struct tdbsam2_object *obj, const char* name)
250 {
251
252         NTSTATUS ret;
253         TDB_DATA data, key;
254         fstring keystr;
255         fstring objname;
256         DOM_SID sid;
257         char *obj_sidstr;
258         int obj_version, obj_type, obj_sidstr_len, len;
259
260         if (!obj || !mem_ctx || !name)
261                 return NT_STATUS_INVALID_PARAMETER;
262
263         if (NT_STATUS_IS_ERR(ret = opentdb())) {
264                 return ret;
265         }
266
267         unix_strlower(name, -1, objname, sizeof(objname));
268
269         slprintf(keystr, sizeof(keystr)-1, "%s%s", NAMEPREFIX, objname);
270         key.dptr = keystr;
271         key.dsize = strlen(keystr) + 1;
272
273         data = tdb_fetch(tdbsam2_db, key);
274         if (!data.dptr) {
275                 DEBUG(5, ("get_object_by_name: Error fetching database, domain entry not found!\n"));
276                 DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
277                 DEBUGADD(5, (" Key: %s\n", keystr));
278                 return NT_STATUS_UNSUCCESSFUL;
279         }
280
281         len = tdb_unpack(data.dptr, data.dsize, TDB_FORMAT_STRING,
282                 &obj_version,
283                 &obj_type,
284                 &obj_sidstr_len, &obj_sidstr);
285
286         SAFE_FREE(data.dptr);
287
288         if (len == -1 || obj_version != TDBSAM_VERSION || obj_sidstr_len <= 0) {
289                 DEBUG(5, ("get_object_by_name: Error unpacking database object!\n"));
290                 return NT_STATUS_UNSUCCESSFUL;
291         }
292
293         if (!string_to_sid(&sid, obj_sidstr)) {
294                 DEBUG(5, ("get_object_by_name: Error invalid sid string found in database object!\n"));
295                 SAFE_FREE(obj_sidstr);
296                 return NT_STATUS_UNSUCCESSFUL;
297         }
298         SAFE_FREE(obj_sidstr);
299         
300         return get_object_by_sid(mem_ctx, obj, &sid);
301 }
302
303 static NTSTATUS store_object(TALLOC_CTX *mem_ctx, struct tdbsam2_object *object, BOOL new_obj)
304 {
305
306         NTSTATUS ret;
307         TDB_DATA data, key, key2;
308         fstring keystr;
309         fstring namestr;
310         int flag, r;
311
312         if (NT_STATUS_IS_ERR(ret = opentdb())) {
313                 return ret;
314         }
315
316         if (new_obj) {
317                 flag = TDB_INSERT;
318         } else {
319                 flag = TDB_MODIFY;
320         }
321
322         ret = init_buffer_from_tdbsam2_object(&(data.dptr), &(data.dsize), mem_ctx, object);
323         if (NT_STATUS_IS_ERR(ret))
324                 return ret;
325
326         switch (object->type) {
327                 case GUMS_OBJ_DOMAIN:
328                         slprintf(keystr, sizeof(keystr) - 1, "%s%s", SIDPREFIX, sid_string_static(object->data.domain->dom_sid));
329                         slprintf(namestr, sizeof(namestr) - 1, "%s%s", NAMEPREFIX, object->data.domain->name);
330                         break;
331                 case GUMS_OBJ_GROUP:
332                 case GUMS_OBJ_ALIAS:
333                         slprintf(keystr, sizeof(keystr) - 1, "%s%s", SIDPREFIX, sid_string_static(object->data.group->group_sid));
334                         slprintf(namestr, sizeof(namestr) - 1, "%s%s", NAMEPREFIX, object->data.group->name);
335                         break;
336                 case GUMS_OBJ_NORMAL_USER:
337                         slprintf(keystr, sizeof(keystr) - 1, "%s%s", SIDPREFIX, sid_string_static(object->data.user->user_sid));
338                         slprintf(namestr, sizeof(namestr) - 1, "%s%s", NAMEPREFIX, object->data.user->name);
339                         break;
340                 default:
341                         return NT_STATUS_UNSUCCESSFUL;  
342         }
343
344         key.dptr = keystr;
345         key.dsize = strlen(keystr) + 1;
346
347         if ((r = tdb_store(tdbsam2_db, key, data, flag)) != TDB_SUCCESS) {
348                 DEBUG(0, ("store_object: Unable to modify SAM!\n"));
349                 DEBUGADD(0, (" Error: %s", tdb_errorstr(tdbsam2_db)));
350                 DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
351                 if (r == TDB_ERR_EXISTS) return NT_STATUS_UNSUCCESSFUL;
352                 return NT_STATUS_INTERNAL_DB_ERROR;
353         }
354
355         key2.dptr = namestr;
356         key2.dsize = strlen(namestr) + 1;
357
358         if ((r = tdb_store(tdbsam2_db, key2, key, flag)) != TDB_SUCCESS) {
359                 DEBUG(0, ("store_object: Unable to modify SAM!\n"));
360                 DEBUGADD(0, (" Error: %s", tdb_errorstr(tdbsam2_db)));
361                 DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
362                 if (r == TDB_ERR_EXISTS) return NT_STATUS_UNSUCCESSFUL;
363                 return NT_STATUS_INTERNAL_DB_ERROR;
364         }
365 /* TODO: update the general database counter */
366 /* TODO: update this entry counter too */
367
368         return NT_STATUS_OK;
369 }
370
371 static NTSTATUS get_next_sid(TALLOC_CTX *mem_ctx, DOM_SID **sid)
372 {
373         NTSTATUS ret;
374         struct tdbsam2_object obj;
375         DOM_SID *dom_sid = get_global_sam_sid();
376         uint32 new_rid;
377
378 /* TODO: LOCK DOMAIN OBJECT */
379         ret = get_object_by_sid(mem_ctx, &obj, dom_sid);
380         if (NT_STATUS_IS_ERR(ret)) {
381                 DEBUG(0, ("get_next_sid: unable to get root Domain object!\n"));
382                 ret = NT_STATUS_INTERNAL_DB_ERROR;
383                 goto error;
384         }
385
386         new_rid = obj.data.domain->next_rid;
387         
388         /* Increment the RID Counter */
389         obj.data.domain->next_rid++;
390         
391         /* Store back Domain object */
392         ret = store_object(mem_ctx, &obj, False);
393         if (NT_STATUS_IS_ERR(ret)) {
394                 DEBUG(0, ("get_next_sid: unable to update root Domain object!\n"));
395                 ret = NT_STATUS_INTERNAL_DB_ERROR;
396                 goto error;
397         }
398 /* TODO: UNLOCK DOMAIN OBJECT */
399
400         *sid = sid_dup_talloc(mem_ctx, dom_sid);
401         TALLOC_CHECK(*sid, ret, error);
402         
403         if (!sid_append_rid(*sid, new_rid)) {
404                 DEBUG(0, ("get_next_sid: unable to build new SID !?!\n"));
405                 ret = NT_STATUS_UNSUCCESSFUL;
406                 goto error;
407         }
408
409         return NT_STATUS_OK;
410
411 error:
412         return ret;
413 }
414
415 static NTSTATUS user_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_user_data *userdata)
416 {
417         NTSTATUS ret;
418
419         if (!object || !userdata) {
420                 DEBUG(0, ("tdbsam2_user_data_to_gums_object: no NULL pointers are accepted here!\n"));
421                 return NT_STATUS_UNSUCCESSFUL;
422         }
423
424         /* userdata->xcounter */
425         /* userdata->sec_desc */
426
427         SET_OR_FAIL(gums_set_object_sid(*object, userdata->user_sid), error);
428         SET_OR_FAIL(gums_set_object_name(*object, userdata->name), error);
429
430         SET_OR_FAIL(gums_set_user_pri_group(*object, userdata->group_sid), error);
431
432         if (userdata->description)
433                 SET_OR_FAIL(gums_set_object_description(*object, userdata->description), error);
434
435         if (userdata->full_name)
436                 SET_OR_FAIL(gums_set_user_fullname(*object, userdata->full_name), error);
437         
438         if (userdata->home_dir)
439                 SET_OR_FAIL(gums_set_user_homedir(*object, userdata->home_dir), error);
440
441         if (userdata->dir_drive)
442                 SET_OR_FAIL(gums_set_user_dir_drive(*object, userdata->dir_drive), error);
443
444         if (userdata->logon_script)
445                 SET_OR_FAIL(gums_set_user_logon_script(*object, userdata->logon_script), error);
446         
447         if (userdata->profile_path) 
448                 SET_OR_FAIL(gums_set_user_profile_path(*object, userdata->profile_path), error);
449
450         if (userdata->workstations)
451                 SET_OR_FAIL(gums_set_user_workstations(*object, userdata->workstations), error);
452
453         if (userdata->unknown_str)
454                 SET_OR_FAIL(gums_set_user_unknown_str(*object, userdata->unknown_str), error);
455
456         if (userdata->munged_dial)
457                 SET_OR_FAIL(gums_set_user_munged_dial(*object, userdata->munged_dial), error);
458
459         SET_OR_FAIL(gums_set_user_logon_divs(*object, userdata->logon_divs), error);
460         SET_OR_FAIL(gums_set_user_hours_len(*object, userdata->hours_len), error);
461
462         if (userdata->hours)
463                 SET_OR_FAIL(gums_set_user_hours(*object, userdata->hours), error);
464
465         SET_OR_FAIL(gums_set_user_unknown_3(*object, userdata->unknown_3), error);
466         SET_OR_FAIL(gums_set_user_unknown_5(*object, userdata->unknown_5), error);
467         SET_OR_FAIL(gums_set_user_unknown_6(*object, userdata->unknown_6), error);
468
469         SET_OR_FAIL(gums_set_user_logon_time(*object, *(userdata->logon_time)), error);
470         SET_OR_FAIL(gums_set_user_logoff_time(*object, *(userdata->logoff_time)), error);
471         SET_OR_FAIL(gums_set_user_kickoff_time(*object, *(userdata->kickoff_time)), error);
472         SET_OR_FAIL(gums_set_user_pass_last_set_time(*object, *(userdata->pass_last_set_time)), error);
473         SET_OR_FAIL(gums_set_user_pass_can_change_time(*object, *(userdata->pass_can_change_time)), error);
474         SET_OR_FAIL(gums_set_user_pass_must_change_time(*object, *(userdata->pass_must_change_time)), error);
475
476         ret = NT_STATUS_OK;
477         return ret;
478         
479 error:
480         talloc_destroy((*object)->mem_ctx);
481         *object = NULL;
482         return ret;
483 }
484
485 static NTSTATUS group_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_group_data *groupdata)
486 {
487         NTSTATUS ret;
488
489         if (!object || !groupdata) {
490                 DEBUG(0, ("tdbsam2_group_data_to_gums_object: no NULL pointers are accepted here!\n"));
491                 return NT_STATUS_UNSUCCESSFUL;
492         }
493
494         /* groupdata->xcounter */
495         /* groupdata->sec_desc */
496
497         SET_OR_FAIL(gums_set_object_sid(*object, groupdata->group_sid), error);
498         SET_OR_FAIL(gums_set_object_name(*object, groupdata->name), error);
499
500         if (groupdata->description)
501                 SET_OR_FAIL(gums_set_object_description(*object, groupdata->description), error);
502
503         if (groupdata->count)
504                 SET_OR_FAIL(gums_set_group_members(*object, groupdata->count, groupdata->members), error);
505
506         ret = NT_STATUS_OK;
507         return ret;
508         
509 error:
510         talloc_destroy((*object)->mem_ctx);
511         *object = NULL;
512         return ret;
513 }
514
515 static NTSTATUS domain_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_domain_data *domdata)
516 {
517
518         NTSTATUS ret;
519
520         if (!object || !*object || !domdata) {
521                 DEBUG(0, ("tdbsam2_domain_data_to_gums_object: no NULL pointers are accepted here!\n"));
522                 return NT_STATUS_UNSUCCESSFUL;
523         }
524
525         /* domdata->xcounter */
526         /* domdata->sec_desc */
527
528         SET_OR_FAIL(gums_set_object_sid(*object, domdata->dom_sid), error);
529         SET_OR_FAIL(gums_set_object_name(*object, domdata->name), error);
530
531         if (domdata->description)
532                 SET_OR_FAIL(gums_set_object_description(*object, domdata->description), error);
533
534         ret = NT_STATUS_OK;
535         return ret;
536         
537 error:
538         talloc_destroy((*object)->mem_ctx);
539         *object = NULL;
540         return ret;
541 }
542
543 static NTSTATUS data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_object *data)
544 {
545
546         NTSTATUS ret;
547
548         if (!object || !data) {
549                 DEBUG(0, ("tdbsam2_user_data_to_gums_object: no NULL structure pointers are accepted here!\n"));
550                 ret = NT_STATUS_INVALID_PARAMETER;
551                 goto done;
552         }
553
554         ret = gums_create_object(object, data->type);
555         if (NT_STATUS_IS_ERR(ret)) {
556                 DEBUG(5, ("tdbsam2_user_data_to_gums_object: error creating gums object!\n"));
557                 goto done;
558         }
559
560         switch (data->type) {
561                 case GUMS_OBJ_DOMAIN:
562                         ret = domain_data_to_gums_object(object, data->data.domain);
563                         break;
564
565                 case GUMS_OBJ_NORMAL_USER:
566                         ret = user_data_to_gums_object(object, data->data.user);
567                         break;
568
569                 case GUMS_OBJ_GROUP:
570                 case GUMS_OBJ_ALIAS:
571                         ret = group_data_to_gums_object(object, data->data.group);
572                         break;
573
574                 default:
575                         ret = NT_STATUS_UNSUCCESSFUL;
576         }
577
578 done:
579         return ret;
580 }
581
582
583 /* GUMM object functions */
584
585 static NTSTATUS tdbsam2_get_domain_sid(DOM_SID *sid, const char* name)
586 {
587
588         NTSTATUS ret;
589         struct tdbsam2_object obj;
590         TALLOC_CTX *mem_ctx;
591         fstring domname;
592
593         if (!sid || !name)
594                 return NT_STATUS_INVALID_PARAMETER;
595
596         mem_ctx = talloc_init("tdbsam2_get_domain_sid");
597         if (!mem_ctx) {
598                 DEBUG(0, ("tdbsam2_new_object: Out of memory!\n"));
599                 return NT_STATUS_NO_MEMORY;
600         }
601
602         if (NT_STATUS_IS_ERR(ret = opentdb())) {
603                 goto done;
604         }
605
606         unix_strlower(name, -1, domname, sizeof(domname));
607
608         ret = get_object_by_name(mem_ctx, &obj, domname);
609
610         if (NT_STATUS_IS_ERR(ret)) {
611                 DEBUG(0, ("tdbsam2_get_domain_sid: Error fetching database!\n"));
612                 goto done;
613         }
614
615         if (obj.type != GUMS_OBJ_DOMAIN) {
616                 DEBUG(5, ("tdbsam2_get_domain_sid: Requested object is not a domain!\n"));
617                 ret = NT_STATUS_UNSUCCESSFUL;
618                 goto done;
619         }
620
621         sid_copy(sid, obj.data.domain->dom_sid);
622
623         ret = NT_STATUS_OK;
624
625 done:
626         talloc_destroy(mem_ctx);
627         return ret;
628 }
629
630 static NTSTATUS tdbsam2_set_domain_sid (const DOM_SID *sid, const char *name)
631 {
632
633         NTSTATUS ret;
634         struct tdbsam2_object obj;
635         TALLOC_CTX *mem_ctx;
636         fstring domname;
637
638         if (!sid || !name)
639                 return NT_STATUS_INVALID_PARAMETER;
640
641         mem_ctx = talloc_init("tdbsam2_set_domain_sid");
642         if (!mem_ctx) {
643                 DEBUG(0, ("tdbsam2_new_object: Out of memory!\n"));
644                 return NT_STATUS_NO_MEMORY;
645         }
646
647         if (tdbsam2_db == NULL) {
648                 if (NT_STATUS_IS_ERR(ret = opentdb())) {
649                         goto done;
650                 }
651         }
652
653         unix_strlower(name, -1, domname, sizeof(domname));
654
655 /* TODO: we need to lock this entry until updated! */
656
657         ret = get_object_by_name(mem_ctx, &obj, domname);
658
659         if (NT_STATUS_IS_ERR(ret)) {
660                 DEBUG(0, ("tdbsam2_get_domain_sid: Error fetching database!\n"));
661                 goto done;
662         }
663
664         if (obj.type != GUMS_OBJ_DOMAIN) {
665                 DEBUG(5, ("tdbsam2_get_domain_sid: Requested object is not a domain!\n"));
666                 ret = NT_STATUS_UNSUCCESSFUL;
667                 goto done;
668         }
669
670         sid_copy(obj.data.domain->dom_sid, sid);
671
672         ret = store_object(mem_ctx, &obj, False);
673
674 done:
675 /* TODO: unlock here */
676         if (mem_ctx) talloc_destroy(mem_ctx);
677         return ret;
678 }
679
680 /* TODO */
681         NTSTATUS (*get_sequence_number) (void);
682
683
684 extern DOM_SID global_sid_NULL;
685
686 static NTSTATUS tdbsam2_new_object(DOM_SID *sid, const char *name, const int obj_type)
687 {
688
689         NTSTATUS ret;
690         struct tdbsam2_object obj;
691         TALLOC_CTX *mem_ctx;
692         NTTIME zero_time = {0,0};
693         const char *defpw = "NOPASSWORDXXXXXX";
694         uint8 defhours[21] = {255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255};
695
696         if (!sid || !name) {
697                 DEBUG(0, ("tdbsam2_new_object: no NULL pointers are accepted here!\n"));
698                 return NT_STATUS_INVALID_PARAMETER;
699         }
700
701         mem_ctx = talloc_init("tdbsam2_new_object");
702         if (!mem_ctx) {
703                 DEBUG(0, ("tdbsam2_new_object: Out of memory!\n"));
704                 return NT_STATUS_NO_MEMORY;
705         }
706
707         obj.type = obj_type;
708         obj.version = TDBSAM_VERSION;
709
710         switch (obj_type) {
711                 case GUMS_OBJ_NORMAL_USER:
712                         obj.data.user = (struct tdbsam2_user_data *)talloc_zero(mem_ctx, sizeof(struct tdbsam2_user_data));
713                         TALLOC_CHECK(obj.data.user, ret, done);
714
715                         get_next_sid(mem_ctx, &(obj.data.user->user_sid));
716                         TALLOC_CHECK(obj.data.user->user_sid, ret, done);
717                         sid_copy(sid, obj.data.user->user_sid);
718
719                         obj.data.user->name = talloc_strdup(mem_ctx, name);
720                         TALLOC_CHECK(obj.data.user, ret, done);
721
722                         obj.data.user->xcounter = 1;
723                         /*obj.data.user->sec_desc*/
724                         obj.data.user->description = "";
725                         obj.data.user->group_sid = &global_sid_NULL;
726                         obj.data.user->logon_time = &zero_time;
727                         obj.data.user->logoff_time = &zero_time;
728                         obj.data.user->kickoff_time = &zero_time;
729                         obj.data.user->pass_last_set_time = &zero_time;
730                         obj.data.user->pass_can_change_time = &zero_time;
731                         obj.data.user->pass_must_change_time = &zero_time;
732
733                         obj.data.user->full_name = "";          
734                         obj.data.user->home_dir = "";           
735                         obj.data.user->dir_drive = "";          
736                         obj.data.user->logon_script = "";               
737                         obj.data.user->profile_path = "";               
738                         obj.data.user->workstations = "";               
739                         obj.data.user->unknown_str = "";                
740                         obj.data.user->munged_dial = "";                
741
742                         obj.data.user->lm_pw_ptr = defpw;
743                         obj.data.user->nt_pw_ptr = defpw;
744
745                         obj.data.user->logon_divs = 168;
746                         obj.data.user->hours_len = 21;
747                         obj.data.user->hours = &defhours;
748
749                         obj.data.user->unknown_3 = 0x00ffffff;
750                         obj.data.user->unknown_5 = 0x00020000;
751                         obj.data.user->unknown_6 = 0x000004ec;
752                         break;
753
754                 case GUMS_OBJ_GROUP:
755                 case GUMS_OBJ_ALIAS:
756                         obj.data.group = (struct tdbsam2_group_data *)talloc_zero(mem_ctx, sizeof(struct tdbsam2_group_data));
757                         TALLOC_CHECK(obj.data.group, ret, done);
758
759                         get_next_sid(mem_ctx, &(obj.data.group->group_sid));
760                         TALLOC_CHECK(obj.data.group->group_sid, ret, done);
761                         sid_copy(sid, obj.data.group->group_sid);
762
763                         obj.data.group->name = talloc_strdup(mem_ctx, name);
764                         TALLOC_CHECK(obj.data.group, ret, done);
765
766                         obj.data.group->xcounter = 1;
767                         /*obj.data.group->sec_desc*/
768                         obj.data.group->description = "";
769
770                         break;
771
772                 case GUMS_OBJ_DOMAIN:
773
774                         /* FIXME: should we check against global_sam_sid to make it impossible
775                                   to store more than one domain ? */ 
776
777                         obj.data.domain = (struct tdbsam2_domain_data *)talloc_zero(mem_ctx, sizeof(struct tdbsam2_domain_data));
778                         TALLOC_CHECK(obj.data.domain, ret, done);
779
780                         obj.data.domain->dom_sid = sid_dup_talloc(mem_ctx, get_global_sam_sid());
781                         TALLOC_CHECK(obj.data.domain->dom_sid, ret, done);
782                         sid_copy(sid, obj.data.domain->dom_sid);
783
784                         obj.data.domain->name = talloc_strdup(mem_ctx, name);
785                         TALLOC_CHECK(obj.data.domain, ret, done);
786
787                         obj.data.domain->xcounter = 1;
788                         /*obj.data.domain->sec_desc*/
789                         obj.data.domain->next_rid = 0x3e9;
790                         obj.data.domain->description = "";
791
792                         ret = NT_STATUS_OK;
793                         break;  
794
795                 default:
796                         ret = NT_STATUS_UNSUCCESSFUL;
797                         goto done;
798         }
799
800         ret = store_object(mem_ctx, &obj, True);
801
802 done:
803         talloc_destroy(mem_ctx);
804         return ret;
805 }
806
807 static NTSTATUS tdbsam2_delete_object(const DOM_SID *sid)
808 {
809         NTSTATUS ret;
810         struct tdbsam2_object obj;
811         TALLOC_CTX *mem_ctx;
812         TDB_DATA data, key;
813         fstring keystr;
814
815         if (!sid) {
816                 DEBUG(0, ("tdbsam2_delete_object: no NULL pointers are accepted here!\n"));
817                 return NT_STATUS_INVALID_PARAMETER;
818         }
819
820         mem_ctx = talloc_init("tdbsam2_delete_object");
821         if (!mem_ctx) {
822                 DEBUG(0, ("tdbsam2_delete_object: Out of memory!\n"));
823                 return NT_STATUS_NO_MEMORY;
824         }
825
826         if (tdbsam2_db == NULL) {
827                 if (NT_STATUS_IS_ERR(ret = opentdb())) {
828                         goto done;
829                 }
830         }
831
832         slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sid_string_static(sid));
833         key.dptr = keystr;
834         key.dsize = strlen(keystr) + 1;
835
836         data = tdb_fetch(tdbsam2_db, key);
837         if (!data.dptr) {
838                 DEBUG(5, ("tdbsam2_delete_object: Error fetching database, SID entry not found!\n"));
839                 DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
840                 DEBUGADD(5, (" Key: %s\n", keystr));
841                 ret = NT_STATUS_UNSUCCESSFUL;
842                 goto done;
843         }
844
845         if (tdb_delete(tdbsam2_db, key) != TDB_SUCCESS) {
846                 DEBUG(5, ("tdbsam2_delete_object: Error deleting object!\n"));
847                 DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
848                 DEBUGADD(5, (" Key: %s\n", keystr));
849                 ret = NT_STATUS_UNSUCCESSFUL;
850                 goto done;
851         }       
852
853         if (NT_STATUS_IS_ERR(init_tdbsam2_object_from_buffer(&obj, mem_ctx, data.dptr, data.dsize))) {
854                 SAFE_FREE(data.dptr);
855                 DEBUG(0, ("tdbsam2_delete_object: Error fetching database, malformed entry!\n"));
856                 ret = NT_STATUS_UNSUCCESSFUL;
857                 goto done;
858         }
859
860         switch (obj.type) {
861                 case GUMS_OBJ_DOMAIN:
862                         /* TODO: SHOULD WE ALLOW TO DELETE DOMAINS ? */
863                         slprintf(keystr, sizeof(keystr) - 1, "%s%s", NAMEPREFIX, obj.data.domain->name);
864                         break;
865                 case GUMS_OBJ_GROUP:
866                 case GUMS_OBJ_ALIAS:
867                         slprintf(keystr, sizeof(keystr) - 1, "%s%s", NAMEPREFIX, obj.data.group->name);
868                         break;
869                 case GUMS_OBJ_NORMAL_USER:
870                         slprintf(keystr, sizeof(keystr) - 1, "%s%s", NAMEPREFIX, obj.data.user->name);
871                         break;
872                 default:
873                         ret = NT_STATUS_UNSUCCESSFUL;
874                         goto done;
875         }
876
877         key.dptr = keystr;
878         key.dsize = strlen(keystr) + 1;
879
880         if (tdb_delete(tdbsam2_db, key) != TDB_SUCCESS) {
881                 DEBUG(5, ("tdbsam2_delete_object: Error deleting object!\n"));
882                 DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db)));
883                 DEBUGADD(5, (" Key: %s\n", keystr));
884                 ret = NT_STATUS_UNSUCCESSFUL;
885                 goto done;
886         }
887
888 /* TODO: update the general database counter */
889
890 done:
891         SAFE_FREE(data.dptr);
892         talloc_destroy(mem_ctx);
893         return ret;
894 }
895
896 static NTSTATUS tdbsam2_get_object_from_sid(GUMS_OBJECT **object, const DOM_SID *sid, const int obj_type)
897 {
898         NTSTATUS ret;
899         struct tdbsam2_object obj;
900         TALLOC_CTX *mem_ctx;
901
902         if (!object || !sid) {
903                 DEBUG(0, ("tdbsam2_get_object_from_sid: no NULL pointers are accepted here!\n"));
904                 return NT_STATUS_INVALID_PARAMETER;
905         }
906
907         mem_ctx = talloc_init("tdbsam2_get_object_from_sid");
908         if (!mem_ctx) {
909                 DEBUG(0, ("tdbsam2_get_object_from_sid: Out of memory!\n"));
910                 return NT_STATUS_NO_MEMORY;
911         }
912
913         ret = get_object_by_sid(mem_ctx, &obj, sid);
914         if (NT_STATUS_IS_ERR(ret) || (obj_type && obj.type != obj_type)) {
915                 DEBUG(0, ("tdbsam2_get_object_from_sid: error fetching object or wrong object type!\n"));
916                 goto done;
917         }
918
919         ret = data_to_gums_object(object, &obj);
920         if (NT_STATUS_IS_ERR(ret)) {
921                 DEBUG(0, ("tdbsam2_get_object_from_sid: error setting object data!\n"));
922                 goto done;
923         }
924         
925 done:
926         talloc_destroy(mem_ctx);
927         return ret;
928 }
929
930 static NTSTATUS tdbsam2_get_object_from_name(GUMS_OBJECT **object, const char *name, const int obj_type)
931 {
932         NTSTATUS ret;
933         struct tdbsam2_object obj;
934         TALLOC_CTX *mem_ctx;
935
936         if (!object || !name) {
937                 DEBUG(0, ("tdbsam2_get_object_from_sid: no NULL pointers are accepted here!\n"));
938                 return NT_STATUS_INVALID_PARAMETER;
939         }
940
941         mem_ctx = talloc_init("tdbsam2_get_object_from_sid");
942         if (!mem_ctx) {
943                 DEBUG(0, ("tdbsam2_get_object_from_sid: Out of memory!\n"));
944                 return NT_STATUS_NO_MEMORY;
945         }
946
947         ret = get_object_by_name(mem_ctx, &obj, name);
948         if (NT_STATUS_IS_ERR(ret) || (obj_type && obj.type != obj_type)) {
949                 DEBUG(0, ("tdbsam2_get_object_from_sid: error fetching object or wrong object type!\n"));
950                 goto done;
951         }
952
953         ret = data_to_gums_object(object, &obj);
954         if (NT_STATUS_IS_ERR(ret)) {
955                 DEBUG(0, ("tdbsam2_get_object_from_sid: error setting object data!\n"));
956                 goto done;
957         }
958         
959 done:
960         talloc_destroy(mem_ctx);
961         return ret;
962 }
963
964         /* This function is used to get the list of all objects changed since base_time, it is
965            used to support PDC<->BDC synchronization */
966         NTSTATUS (*get_updated_objects) (GUMS_OBJECT **objects, const NTTIME base_time);
967
968 static NTSTATUS tdbsam2_enumerate_objects_start(void *handle, const DOM_SID *sid, const int obj_type)
969 {
970         struct tdbsam2_enum_objs *teo, *t;
971         pstring tdbfile;
972
973         teo = (struct tdbsam2_enum_objs *)calloc(1, sizeof(struct tdbsam2_enum_objs));
974         if (!teo) {
975                 DEBUG(0, ("tdbsam2_enumerate_objects_start: Out of Memory!\n"));
976                 return NT_STATUS_NO_MEMORY;
977         }
978
979         teo->type = obj_type;
980         if (sid) {
981                 sid_to_string(teo->dom_sid, sid);
982         }
983
984         get_private_directory(tdbfile);
985         pstrcat(tdbfile, "/");
986         pstrcat(tdbfile, TDB_FILE_NAME);
987
988         teo->db = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDONLY, 0600);
989         if (!teo->db)
990         {
991                 DEBUG(0, ("tdbsam2_enumerate_objects_start: Unable to open database (%s)!\n", tdbfile));
992                 SAFE_FREE(teo);
993                 return NT_STATUS_UNSUCCESSFUL;
994         }
995
996         if (!teo_handlers) {
997                 *teo_handlers = teo;
998         } else {
999                 t = *teo_handlers;
1000                 while (t->next) {
1001                         t = t->next;
1002                 }
1003                 t->next = teo;
1004         }
1005
1006         handle = teo;
1007
1008         teo->key = tdb_firstkey(teo->db);
1009
1010         return NT_STATUS_OK;    
1011 }
1012
1013 static NTSTATUS tdbsam2_enumerate_objects_get_next(GUMS_OBJECT **object, void *handle)
1014 {
1015         NTSTATUS ret;
1016         TALLOC_CTX *mem_ctx;
1017         TDB_DATA data;
1018         struct tdbsam2_enum_objs *teo;
1019         struct tdbsam2_object obj;
1020         const char *prefix = SIDPREFIX;
1021         const int preflen = strlen(prefix);
1022
1023         if (!object || !handle) {
1024                 DEBUG(0, ("tdbsam2_get_object_from_sid: no NULL pointers are accepted here!\n"));
1025                 return NT_STATUS_INVALID_PARAMETER;
1026         }
1027
1028         teo = (struct tdbsam2_enum_objs *)handle;
1029
1030         mem_ctx = talloc_init("tdbsam2_enumerate_objects_get_next");
1031         if (!mem_ctx) {
1032                 DEBUG(0, ("tdbsam2_enumerate_objects_get_next: Out of memory!\n"));
1033                 return NT_STATUS_NO_MEMORY;
1034         }
1035
1036         while ((teo->key.dsize != 0)) {
1037                 int len, version, type, size;
1038                 char *ptr;
1039
1040                 if (strncmp(teo->key.dptr, prefix, preflen)) {
1041                         teo->key = tdb_nextkey(teo->db, teo->key);
1042                         continue;
1043                 }
1044
1045                 if (teo->dom_sid) {
1046                         if (strncmp(&(teo->key.dptr[preflen]), teo->dom_sid, strlen(teo->dom_sid))) {
1047                                 teo->key = tdb_nextkey(teo->db, teo->key);
1048                                 continue;
1049                         }
1050                 }
1051
1052                 data = tdb_fetch(teo->db, teo->key);
1053                 if (!data.dptr) {
1054                         DEBUG(5, ("tdbsam2_enumerate_objects_get_next: Error fetching database, SID entry not found!\n"));
1055                         DEBUGADD(5, (" Error: %s\n", tdb_errorstr(teo->db)));
1056                         DEBUGADD(5, (" Key: %s\n", teo->key.dptr));
1057                         ret = NT_STATUS_UNSUCCESSFUL;
1058                         goto done;
1059                 }
1060
1061                 len = tdb_unpack (data.dptr, data.dsize, TDB_FORMAT_STRING,
1062                           &version,
1063                           &type,
1064                           &size, &ptr);
1065
1066                 if (len == -1) {
1067                         DEBUG(5, ("tdbsam2_enumerate_objects_get_next: Error unable to unpack data!\n"));
1068                         ret = NT_STATUS_UNSUCCESSFUL;
1069                         goto done;
1070                 }
1071                 SAFE_FREE(ptr);
1072
1073                 if (teo->type && type != teo->type) {
1074                         SAFE_FREE(data.dptr);
1075                         data.dsize = 0;
1076                         teo->key = tdb_nextkey(teo->db, teo->key);
1077                         continue;
1078                 }
1079                 
1080                 break;
1081         }
1082
1083         if (data.dsize != 0) {
1084                 if (NT_STATUS_IS_ERR(init_tdbsam2_object_from_buffer(&obj, mem_ctx, data.dptr, data.dsize))) {
1085                         SAFE_FREE(data.dptr);
1086                         DEBUG(0, ("tdbsam2_enumerate_objects_get_next: Error fetching database, malformed entry!\n"));
1087                         ret = NT_STATUS_UNSUCCESSFUL;
1088                         goto done;
1089                 }
1090                 SAFE_FREE(data.dptr);
1091         }
1092
1093         ret = data_to_gums_object(object, &obj);
1094
1095 done:
1096         talloc_destroy(mem_ctx);
1097         return ret;
1098 }
1099
1100 static NTSTATUS tdbsam2_enumerate_objects_stop(void *handle)
1101 {
1102         struct tdbsam2_enum_objs *teo, *t, *p;
1103
1104         teo = (struct tdbsam2_enum_objs *)handle;
1105
1106         if (*teo_handlers == teo) {
1107                 *teo_handlers = teo->next;
1108         } else {
1109                 t = *teo_handlers;
1110                 while (t != teo) {
1111                         p = t;
1112                         t = t->next;
1113                         if (t == NULL) {
1114                                 DEBUG(0, ("tdbsam2_enumerate_objects_stop: Error, handle not found!\n"));
1115                                 return NT_STATUS_UNSUCCESSFUL;
1116                         }
1117                 }
1118                 p = t->next;
1119         }
1120
1121         tdb_close(teo->db);
1122         SAFE_FREE(teo);
1123
1124         return NT_STATUS_OK;
1125 }
1126
1127         /* This function MUST be used ONLY by PDC<->BDC replication code or recovery tools.
1128            Never use this function to update an object in the database, use set_object_values() */
1129         NTSTATUS (*set_object) (const GUMS_OBJECT *object);
1130
1131         /* set object values function */
1132         NTSTATUS (*set_object_values) (DOM_SID *sid, uint32 count, GUMS_DATA_SET *data_set);
1133
1134         /* Group related functions */
1135         NTSTATUS (*add_memberss_to_group) (const DOM_SID *group, const DOM_SID **members);
1136         NTSTATUS (*delete_members_from_group) (const DOM_SID *group, const DOM_SID **members);
1137         NTSTATUS (*enumerate_group_members) (DOM_SID **members, const DOM_SID *sid, const int type);
1138
1139         NTSTATUS (*get_sid_groups) (DOM_SID **groups, const DOM_SID *sid);
1140
1141         NTSTATUS (*lock_sid) (const DOM_SID *sid);
1142         NTSTATUS (*unlock_sid) (const DOM_SID *sid);
1143
1144         /* privileges related functions */
1145
1146         NTSTATUS (*add_members_to_privilege) (const LUID_ATTR *priv, const DOM_SID **members);
1147         NTSTATUS (*delete_members_from_privilege) (const LUID_ATTR *priv, const DOM_SID **members);
1148         NTSTATUS (*enumerate_privilege_members) (DOM_SID **members, const LUID_ATTR *priv);
1149         NTSTATUS (*get_sid_privileges) (DOM_SID **privs, const DOM_SID *sid);
1150         /* warning!: set_privilege will overwrite a prior existing privilege if such exist */
1151         NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv);
1152
1153
1154 int gumm_init(GUMS_FUNCTIONS **storage)
1155 {
1156         tdbsam2_db = NULL;
1157         teo_handlers = 0;
1158
1159         return 0;
1160 }
1161
1162 #if 0
1163 int main(int argc, char *argv[])
1164 {
1165         NTSTATUS ret;
1166         DOM_SID dsid;
1167
1168         if (argc < 2) {
1169                 printf ("not enough arguments!\n");
1170                 exit(0);
1171         }
1172
1173         if (!lp_load(dyn_CONFIGFILE,True,False,False)) {
1174                 fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE);
1175                 exit(1);
1176         }
1177
1178         ret = tdbsam2_new_object(&dsid, "_domain_", GUMS_OBJ_DOMAIN);
1179         if (NT_STATUS_IS_OK(ret)) {
1180                 printf ("_domain_ created, sid=%s\n", sid_string_static(&dsid));
1181         } else {
1182                 printf ("_domain_ creation error n. 0x%08x\n", ret.v);
1183         }
1184         ret = tdbsam2_new_object(&dsid, argv[1], GUMS_OBJ_NORMAL_USER);
1185         if (NT_STATUS_IS_OK(ret)) {
1186                 printf ("%s user created, sid=%s\n", argv[1], sid_string_static(&dsid));
1187         } else {
1188                 printf ("%s user creation error n. 0x%08x\n", argv[1], ret.v);
1189         }
1190         
1191         exit(0);
1192 }
1193 #endif