r5957: BUGS 2478, 2093: compiler warning patches from Jason Mader
[bbaumbach/samba-autobuild/.git] / source3 / passdb / pdb_xml.c
1
2 /*
3  * XML password backend for samba
4  * Copyright (C) Jelmer Vernooij 2002
5  * Some parts based on the libxml gjobread example by Daniel Veillard
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  * 
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  * 
17  * You should have received a copy of the GNU General Public License along with
18  * this program; if not, write to the Free Software Foundation, Inc., 675
19  * Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 /* FIXME: 
23  * - Support stdin input by using '-'
24  * - Be faster. Don't rewrite the whole file when adding a user, but store it in the memory and save it when exiting. Requires changes to samba source.
25  * - Gives the ability to read/write to standard input/output
26  * - Do locking!
27  * - Better names!
28  */
29
30
31 #define XML_URL "http://samba.org/samba/DTD/passdb"
32
33 #include "includes.h"
34
35 #include <libxml/xmlmemory.h>
36 #include <libxml/parser.h>
37
38 static int xmlsam_debug_level = DBGC_ALL;
39
40 #undef DBGC_CLASS
41 #define DBGC_CLASS xmlsam_debug_level
42
43
44 /* Helper utilities for charset conversion */
45 static xmlNodePtr smbXmlNewChild(xmlNodePtr prnt, xmlNsPtr ns, const xmlChar *name, const char *content)
46 {
47         char *string_utf8;
48         xmlNodePtr ret;
49
50         if(!content) return xmlNewChild(prnt, ns, name, NULL);
51
52         
53         if(push_utf8_allocate(&string_utf8,content) == (size_t)-1)
54        return NULL;
55
56         ret = xmlNewTextChild(prnt, ns, name, string_utf8);
57
58         SAFE_FREE(string_utf8);
59
60         return ret;
61 }
62
63
64 static char * iota(int a) {
65         static char tmp[10];
66
67         snprintf(tmp, 9, "%d", a);
68         return tmp;
69 }
70
71 static BOOL parsePass(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
72 {
73         pstring temp;
74
75         cur = cur->xmlChildrenNode;
76         while (cur != NULL) {
77                 if (strcmp(cur->name, "crypt"))
78                         DEBUG(0, ("Unknown element %s\n", cur->name));
79                 else {
80                         if (!strcmp(xmlGetProp(cur, "type"), "nt")
81                                 &&
82                                 pdb_gethexpwd(xmlNodeListGetString
83                                                           (doc, cur->xmlChildrenNode, 1), temp))
84                                 pdb_set_nt_passwd(u, temp, PDB_SET);
85                         else if (!strcmp(xmlGetProp(cur, "type"), "lanman")
86                                          &&
87                                          pdb_gethexpwd(xmlNodeListGetString
88                                                                    (doc, cur->xmlChildrenNode, 1), temp))
89                                 pdb_set_lanman_passwd(u, temp, PDB_SET);
90                         else
91                                 DEBUG(0,
92                                           ("Unknown crypt type: %s\n",
93                                            xmlGetProp(cur, "type")));
94                 }
95                 cur = cur->next;
96         }
97         return True;
98 }
99
100 static BOOL parseUser(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
101 {
102         char *tmp;
103         DOM_SID sid;
104
105         tmp = xmlGetProp(cur, "sid");
106         if (tmp){
107                 string_to_sid(&sid, tmp);
108                 pdb_set_user_sid(u, &sid, PDB_SET);
109         }
110         pdb_set_username(u, xmlGetProp(cur, "name"), PDB_SET);
111         /* We don't care what the top level element name is */
112         cur = cur->xmlChildrenNode;
113         while (cur != NULL) {
114                 if ((!strcmp(cur->name, "group")) && (cur->ns == ns)) {
115                         tmp = xmlGetProp(cur, "sid");
116                         if (tmp){
117                                 string_to_sid(&sid, tmp);
118                                 pdb_set_group_sid(u, &sid, PDB_SET);
119                         }
120                 }
121
122                 else if ((!strcmp(cur->name, "domain")) && (cur->ns == ns))
123                         pdb_set_domain(u,
124                                                    xmlNodeListGetString(doc, cur->xmlChildrenNode,
125                                                                                                 1), PDB_SET);
126
127                 else if (!strcmp(cur->name, "fullname") && cur->ns == ns)
128                         pdb_set_fullname(u,
129                                                          xmlNodeListGetString(doc,
130                                                                                                   cur->xmlChildrenNode,
131                                                                                                   1), PDB_SET);
132
133                 else if (!strcmp(cur->name, "nt_username") && cur->ns == ns)
134                         pdb_set_nt_username(u,
135                                                                 xmlNodeListGetString(doc,
136                                                                                                          cur->xmlChildrenNode,
137                                                                                                          1), PDB_SET);
138
139                 else if (!strcmp(cur->name, "logon_script") && cur->ns == ns)
140                         pdb_set_logon_script(u,
141                                                                  xmlNodeListGetString(doc,
142                                                                                                           cur->xmlChildrenNode,
143                                                                                                           1), PDB_SET);
144
145                 else if (!strcmp(cur->name, "profile_path") && cur->ns == ns)
146                         pdb_set_profile_path(u,
147                                                                  xmlNodeListGetString(doc,
148                                                                                                           cur->xmlChildrenNode,
149                                                                                                           1), PDB_SET);
150
151                 else if (!strcmp(cur->name, "logon_time") && cur->ns == ns)
152                         pdb_set_logon_time(u,
153                                                            atol(xmlNodeListGetString
154                                                                         (doc, cur->xmlChildrenNode, 1)), PDB_SET);
155
156                 else if (!strcmp(cur->name, "logoff_time") && cur->ns == ns)
157                         pdb_set_logoff_time(u,
158                                                                 atol(xmlNodeListGetString
159                                                                          (doc, cur->xmlChildrenNode, 1)),
160                                                                 PDB_SET);
161
162                 else if (!strcmp(cur->name, "kickoff_time") && cur->ns == ns)
163                         pdb_set_kickoff_time(u,
164                                                                  atol(xmlNodeListGetString
165                                                                           (doc, cur->xmlChildrenNode, 1)),
166                                                                  PDB_SET);
167
168                 else if (!strcmp(cur->name, "logon_divs") && cur->ns == ns)
169                         pdb_set_logon_divs(u,
170                                                            atol(xmlNodeListGetString
171                                                                         (doc, cur->xmlChildrenNode, 1)), PDB_SET);
172
173                 else if (!strcmp(cur->name, "hours_len") && cur->ns == ns)
174                         pdb_set_hours_len(u,
175                                                           atol(xmlNodeListGetString
176                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
177
178                 else if (!strcmp(cur->name, "bad_password_count") && cur->ns == ns)
179                         pdb_set_bad_password_count(u,
180                                                           atol(xmlNodeListGetString
181                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
182
183                 else if (!strcmp(cur->name, "logon_count") && cur->ns == ns)
184                         pdb_set_logon_count(u,
185                                                           atol(xmlNodeListGetString
186                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
187
188                 else if (!strcmp(cur->name, "unknown_6") && cur->ns == ns)
189                         pdb_set_unknown_6(u,
190                                                           atol(xmlNodeListGetString
191                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
192
193                 else if (!strcmp(cur->name, "homedir") && cur->ns == ns)
194                         pdb_set_homedir(u,
195                                                         xmlNodeListGetString(doc, cur->xmlChildrenNode,
196                                                                                                  1), PDB_SET);
197
198                 else if (!strcmp(cur->name, "unknown_str") && cur->ns == ns)
199                         pdb_set_unknown_str(u,
200                                                                 xmlNodeListGetString(doc,
201                                                                                                          cur->xmlChildrenNode,
202                                                                                                          1), PDB_SET);
203
204                 else if (!strcmp(cur->name, "dir_drive") && cur->ns == ns)
205                         pdb_set_dir_drive(u,
206                                                           xmlNodeListGetString(doc,
207                                                                                                    cur->xmlChildrenNode,
208                                                                                                    1), PDB_SET);
209
210                 else if (!strcmp(cur->name, "munged_dial") && cur->ns == ns)
211                         pdb_set_munged_dial(u,
212                                                                 xmlNodeListGetString(doc,
213                                                                                                          cur->xmlChildrenNode,
214                                                                                                          1), PDB_SET);
215
216                 else if (!strcmp(cur->name, "acct_desc") && cur->ns == ns)
217                         pdb_set_acct_desc(u,
218                                                           xmlNodeListGetString(doc,
219                                                                                                    cur->xmlChildrenNode,
220                                                                                                    1), PDB_SET);
221
222                 else if (!strcmp(cur->name, "acct_ctrl") && cur->ns == ns)
223                         pdb_set_acct_ctrl(u,
224                                                           atol(xmlNodeListGetString
225                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
226
227                 else if (!strcmp(cur->name, "workstations") && cur->ns == ns)
228                         pdb_set_workstations(u,
229                                                                  xmlNodeListGetString(doc,
230                                                                                                           cur->xmlChildrenNode,
231                                                                                                           1), PDB_SET);
232
233                 else if ((!strcmp(cur->name, "password")) && (cur->ns == ns)) {
234                         tmp = xmlGetProp(cur, "last_set");
235                         if (tmp)
236                                 pdb_set_pass_last_set_time(u, atol(tmp), PDB_SET);
237                         tmp = xmlGetProp(cur, "must_change");
238                         if (tmp)
239                                 pdb_set_pass_must_change_time(u, atol(tmp), PDB_SET);
240                         tmp = xmlGetProp(cur, "can_change");
241                         if (tmp)
242                                 pdb_set_pass_can_change_time(u, atol(tmp), PDB_SET);
243                         parsePass(doc, ns, cur, u);
244                 }
245
246                 else
247                         DEBUG(0, ("Unknown element %s\n", cur->name));
248                 cur = cur->next;
249         }
250
251         return True;
252 }
253
254 typedef struct pdb_xml {
255         char *location;
256         char written;
257         xmlDocPtr doc;
258         xmlNodePtr users;
259         xmlNodePtr pwent;
260         xmlNsPtr ns;
261 } pdb_xml;
262
263 static xmlNodePtr parseSambaXMLFile(struct pdb_xml *data)
264 {
265         xmlNodePtr cur;
266
267         data->doc = xmlParseFile(data->location);
268         if (data->doc == NULL)
269                 return NULL;
270
271         cur = xmlDocGetRootElement(data->doc);
272         if (!cur) {
273                 DEBUG(0, ("empty document\n"));
274                 xmlFreeDoc(data->doc);
275                 return NULL;
276         }
277         data->ns = xmlSearchNsByHref(data->doc, cur, XML_URL);
278         if (!data->ns) {
279                 DEBUG(0,
280                           ("document of the wrong type, samba user namespace not found\n"));
281                 xmlFreeDoc(data->doc);
282                 return NULL;
283         }
284         if (strcmp(cur->name, "samba")) {
285                 DEBUG(0, ("document of the wrong type, root node != samba"));
286                 xmlFreeDoc(data->doc);
287                 return NULL;
288         }
289
290         cur = cur->xmlChildrenNode;
291         while (cur && xmlIsBlankNode(cur)) {
292                 cur = cur->next;
293         }
294         if (!cur)
295                 return NULL;
296         if ((strcmp(cur->name, "users")) || (cur->ns != data->ns)) {
297                 DEBUG(0, ("document of the wrong type, was '%s', users expected",
298                                   cur->name));
299                 DEBUG(0, ("xmlDocDump follows\n"));
300                 xmlDocDump(stderr, data->doc);
301                 DEBUG(0, ("xmlDocDump finished\n"));
302                 xmlFreeDoc(data->doc);
303                 return NULL;
304         }
305         data->users = cur;
306         cur = cur->xmlChildrenNode;
307         return cur;
308 }
309
310 static NTSTATUS xmlsam_setsampwent(struct pdb_methods *methods, BOOL update, uint16 acb_mask)
311 {
312         pdb_xml *data;
313
314         if (!methods) {
315                 DEBUG(0, ("Invalid methods\n"));
316                 return NT_STATUS_INVALID_PARAMETER;
317         }
318         data = (pdb_xml *) methods->private_data;
319         if (!data) {
320                 DEBUG(0, ("Invalid pdb_xml_data\n"));
321                 return NT_STATUS_INVALID_PARAMETER;
322         }
323         data->pwent = parseSambaXMLFile(data);
324         if (!data->pwent)
325                 return NT_STATUS_UNSUCCESSFUL;
326         
327         return NT_STATUS_OK;
328 }
329
330 /***************************************************************
331   End enumeration of the passwd list.
332  ****************************************************************/
333
334 static void xmlsam_endsampwent(struct pdb_methods *methods)
335 {
336         pdb_xml *data;
337
338         if (!methods) {
339                 DEBUG(0, ("Invalid methods\n"));
340                 return;
341         }
342
343         data = (pdb_xml *) methods->private_data;
344
345         if (!data) {
346                 DEBUG(0, ("Invalid pdb_xml_data\n"));
347                 return;
348         }
349
350         xmlFreeDoc(data->doc);
351         data->doc = NULL;
352         data->pwent = NULL;
353 }
354
355 /*****************************************************************
356   Get one SAM_ACCOUNT from the list (next in line)
357  *****************************************************************/
358
359 static NTSTATUS xmlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
360 {
361         pdb_xml *data;
362
363         if (!methods) {
364                 DEBUG(0, ("Invalid methods\n"));
365                 return NT_STATUS_INVALID_PARAMETER;
366         }
367         data = (pdb_xml *) methods->private_data;
368
369         if (!data) {
370                 DEBUG(0, ("Invalid pdb_xml_data\n"));
371                 return NT_STATUS_INVALID_PARAMETER;
372         }
373
374         while (data->pwent) {
375                 if ((!strcmp(data->pwent->name, "user")) &&
376                         (data->pwent->ns == data->ns)) {
377
378                         parseUser(data->doc, data->ns, data->pwent, user);
379                         data->pwent = data->pwent->next;
380                         return NT_STATUS_OK;
381                 }
382                 data->pwent = data->pwent->next;
383         }
384         return NT_STATUS_UNSUCCESSFUL;
385 }
386
387 /***************************************************************************
388   Adds an existing SAM_ACCOUNT
389  ****************************************************************************/
390
391 static NTSTATUS xmlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * u)
392 {
393         pstring temp;
394         fstring sid_str;
395         xmlNodePtr cur, user, pass, root;
396         pdb_xml *data;
397
398         DEBUG(10, ("xmlsam_add_sam_account called!\n"));
399
400         if (!methods) {
401                 DEBUG(0, ("Invalid methods\n"));
402                 return NT_STATUS_INVALID_PARAMETER;
403         }
404
405         data = (pdb_xml *) methods->private_data;
406         if (!data) {
407                 DEBUG(0, ("Invalid pdb_xml_data\n"));
408                 return NT_STATUS_INVALID_PARAMETER;
409         }
410
411         /* Create a new document if we can't open the current one */
412         if (!parseSambaXMLFile(data)) {
413                 DEBUG(0, ("Can't load current XML file, creating a new one\n"));
414                 data->doc = xmlNewDoc(XML_DEFAULT_VERSION);
415                 root = xmlNewDocNode(data->doc, NULL, "samba", NULL);
416                 cur = xmlDocSetRootElement(data->doc, root);
417                 data->ns = xmlNewNs(root, XML_URL, "samba");
418                 data->users = smbXmlNewChild(root, data->ns, "users", NULL);
419         }
420
421         user = smbXmlNewChild(data->users, data->ns, "user", NULL);
422         xmlNewProp(user, "sid",
423                            sid_to_string(sid_str, pdb_get_user_sid(u)));
424
425         if (pdb_get_username(u) && strcmp(pdb_get_username(u), ""))
426                 xmlNewProp(user, "name", pdb_get_username(u));
427
428         cur = smbXmlNewChild(user, data->ns, "group", NULL);
429         
430         xmlNewProp(cur, "sid",
431                            sid_to_string(sid_str, pdb_get_group_sid(u)));
432
433         if (pdb_get_init_flags(u, PDB_LOGONTIME) != PDB_DEFAULT)
434                 smbXmlNewChild(user, data->ns, "logon_time",
435                                         iota(pdb_get_logon_time(u)));
436
437         if (pdb_get_init_flags(u, PDB_LOGOFFTIME) != PDB_DEFAULT)
438                 smbXmlNewChild(user, data->ns, "logoff_time",
439                                         iota(pdb_get_logoff_time(u)));
440
441         if (pdb_get_init_flags(u, PDB_KICKOFFTIME) != PDB_DEFAULT)
442                 smbXmlNewChild(user, data->ns, "kickoff_time",
443                                         iota(pdb_get_kickoff_time(u)));
444
445         if (pdb_get_domain(u) && strcmp(pdb_get_domain(u), ""))
446                 smbXmlNewChild(user, data->ns, "domain", pdb_get_domain(u));
447
448         if (pdb_get_nt_username(u) && strcmp(pdb_get_nt_username(u), ""))
449                 smbXmlNewChild(user, data->ns, "nt_username", pdb_get_nt_username(u));
450
451         if (pdb_get_fullname(u) && strcmp(pdb_get_fullname(u), ""))
452                 smbXmlNewChild(user, data->ns, "fullname", pdb_get_fullname(u));
453
454         if (pdb_get_homedir(u) && strcmp(pdb_get_homedir(u), ""))
455                 smbXmlNewChild(user, data->ns, "homedir", pdb_get_homedir(u));
456
457         if (pdb_get_dir_drive(u) && strcmp(pdb_get_dir_drive(u), ""))
458                 smbXmlNewChild(user, data->ns, "dir_drive", pdb_get_dir_drive(u));
459
460         if (pdb_get_logon_script(u) && strcmp(pdb_get_logon_script(u), ""))
461                 smbXmlNewChild(user, data->ns, "logon_script",
462                                         pdb_get_logon_script(u));
463
464         if (pdb_get_profile_path(u) && strcmp(pdb_get_profile_path(u), ""))
465                 smbXmlNewChild(user, data->ns, "profile_path",
466                                         pdb_get_profile_path(u));
467
468         if (pdb_get_acct_desc(u) && strcmp(pdb_get_acct_desc(u), ""))
469                 smbXmlNewChild(user, data->ns, "acct_desc", pdb_get_acct_desc(u));
470
471         if (pdb_get_workstations(u) && strcmp(pdb_get_workstations(u), ""))
472                 smbXmlNewChild(user, data->ns, "workstations",
473                                         pdb_get_workstations(u));
474
475         if (pdb_get_unknown_str(u) && strcmp(pdb_get_unknown_str(u), ""))
476                 smbXmlNewChild(user, data->ns, "unknown_str", pdb_get_unknown_str(u));
477
478         if (pdb_get_munged_dial(u) && strcmp(pdb_get_munged_dial(u), ""))
479                 smbXmlNewChild(user, data->ns, "munged_dial", pdb_get_munged_dial(u));
480
481
482         /* Password stuff */
483         pass = smbXmlNewChild(user, data->ns, "password", NULL);
484         if (pdb_get_pass_last_set_time(u))
485                 xmlNewProp(pass, "last_set", iota(pdb_get_pass_last_set_time(u)));
486         if (pdb_get_init_flags(u, PDB_CANCHANGETIME) != PDB_DEFAULT)
487                 xmlNewProp(pass, "can_change",
488                                    iota(pdb_get_pass_can_change_time(u)));
489
490         if (pdb_get_init_flags(u, PDB_MUSTCHANGETIME) != PDB_DEFAULT)
491                 xmlNewProp(pass, "must_change",
492                                    iota(pdb_get_pass_must_change_time(u)));
493
494
495         if (pdb_get_lanman_passwd(u)) {
496                 pdb_sethexpwd(temp, pdb_get_lanman_passwd(u),
497                                           pdb_get_acct_ctrl(u));
498                 cur = smbXmlNewChild(pass, data->ns, "crypt", temp);
499                 xmlNewProp(cur, "type", "lanman");
500         }
501
502         if (pdb_get_nt_passwd(u)) {
503                 pdb_sethexpwd(temp, pdb_get_nt_passwd(u), pdb_get_acct_ctrl(u));
504                 cur = smbXmlNewChild(pass, data->ns, "crypt", temp);
505                 xmlNewProp(cur, "type", "nt");
506         }
507
508         smbXmlNewChild(user, data->ns, "acct_ctrl", iota(pdb_get_acct_ctrl(u)));
509
510         if (pdb_get_logon_divs(u))
511                 smbXmlNewChild(user, data->ns, "logon_divs",
512                                         iota(pdb_get_logon_divs(u)));
513
514         if (pdb_get_hours_len(u))
515                 smbXmlNewChild(user, data->ns, "hours_len",
516                                         iota(pdb_get_hours_len(u)));
517
518         smbXmlNewChild(user, data->ns, "bad_password_count", iota(pdb_get_bad_password_count(u)));
519         smbXmlNewChild(user, data->ns, "logon_count", iota(pdb_get_logon_count(u)));
520         smbXmlNewChild(user, data->ns, "unknown_6", iota(pdb_get_unknown_6(u)));
521         xmlSaveFile(data->location, data->doc);
522
523         return NT_STATUS_OK;
524 }
525
526 static NTSTATUS xmlsam_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method,
527                  const char *location)
528 {
529         NTSTATUS nt_status;
530         pdb_xml *data;
531
532         xmlsam_debug_level = debug_add_class("xmlsam");
533         if (xmlsam_debug_level == -1) {
534                 xmlsam_debug_level = DBGC_ALL;
535                 DEBUG(0, ("xmlsam: Couldn't register custom debugging class!\n"));
536         }
537
538         if (!pdb_context) {
539                 DEBUG(0, ("invalid pdb_methods specified\n"));
540                 return NT_STATUS_UNSUCCESSFUL;
541         }
542
543         if (!NT_STATUS_IS_OK
544                 (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
545                 return nt_status;
546         }
547
548         (*pdb_method)->name = "xmlsam";
549
550         (*pdb_method)->setsampwent = xmlsam_setsampwent;
551         (*pdb_method)->endsampwent = xmlsam_endsampwent;
552         (*pdb_method)->getsampwent = xmlsam_getsampwent;
553         (*pdb_method)->add_sam_account = xmlsam_add_sam_account;
554         (*pdb_method)->getsampwnam = NULL;
555         (*pdb_method)->getsampwsid = NULL;
556         (*pdb_method)->update_sam_account = NULL;
557         (*pdb_method)->delete_sam_account = NULL;
558         (*pdb_method)->getgrsid = NULL;
559         (*pdb_method)->getgrgid = NULL;
560         (*pdb_method)->getgrnam = NULL;
561         (*pdb_method)->add_group_mapping_entry = NULL;
562         (*pdb_method)->update_group_mapping_entry = NULL;
563         (*pdb_method)->delete_group_mapping_entry = NULL;
564         (*pdb_method)->enum_group_mapping = NULL;
565
566         data = talloc(pdb_context->mem_ctx, sizeof(pdb_xml));
567         data->location = talloc_strdup(pdb_context->mem_ctx, (location ? location : "passdb.xml"));
568         data->pwent = NULL;
569         data->written = 0;
570         (*pdb_method)->private_data = data;
571
572         LIBXML_TEST_VERSION xmlKeepBlanksDefault(0);
573
574         return NT_STATUS_OK;
575 }
576
577 NTSTATUS pdb_xml_init(void) 
578 {
579         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "xml", xmlsam_init);
580 }