Doc updates from John + some minor fixes by me
[samba.git] / source3 / modules / 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://www.samba.org/ns"
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 static char * iota(int a) {
44         static char tmp[10];
45
46         snprintf(tmp, 9, "%d", a);
47         return tmp;
48 }
49
50 static BOOL parsePass(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
51 {
52         pstring temp;
53
54         cur = cur->xmlChildrenNode;
55         while (cur != NULL) {
56                 if (strcmp(cur->name, "crypt"))
57                         DEBUG(0, ("Unknown element %s\n", cur->name));
58                 else {
59                         if (!strcmp(xmlGetProp(cur, "type"), "nt")
60                                 &&
61                                 pdb_gethexpwd(xmlNodeListGetString
62                                                           (doc, cur->xmlChildrenNode, 1), temp))
63                                 pdb_set_nt_passwd(u, temp, PDB_SET);
64                         else if (!strcmp(xmlGetProp(cur, "type"), "lanman")
65                                          &&
66                                          pdb_gethexpwd(xmlNodeListGetString
67                                                                    (doc, cur->xmlChildrenNode, 1), temp))
68                                 pdb_set_lanman_passwd(u, temp, PDB_SET);
69                         else
70                                 DEBUG(0,
71                                           ("Unknown crypt type: %s\n",
72                                            xmlGetProp(cur, "type")));
73                 }
74                 cur = cur->next;
75         }
76         return True;
77 }
78
79 static BOOL parseUser(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
80 {
81         char *tmp;
82         DOM_SID sid;
83
84         tmp = xmlGetProp(cur, "sid");
85         if (tmp){
86                 string_to_sid(&sid, tmp);
87                 pdb_set_user_sid(u, &sid, PDB_SET);
88         }
89         tmp = xmlGetProp(cur, "uid");
90         if (tmp)
91                 pdb_set_uid(u, atol(tmp), PDB_SET);
92         pdb_set_username(u, xmlGetProp(cur, "name"), PDB_SET);
93         /* We don't care what the top level element name is */
94         cur = cur->xmlChildrenNode;
95         while (cur != NULL) {
96                 if ((!strcmp(cur->name, "group")) && (cur->ns == ns)) {
97                         tmp = xmlGetProp(cur, "gid");
98                         if (tmp)
99                                 pdb_set_gid(u, atol(tmp), PDB_SET);
100                         tmp = xmlGetProp(cur, "sid");
101                         if (tmp){
102                                 string_to_sid(&sid, tmp);
103                                 pdb_set_group_sid(u, &sid, PDB_SET);
104                         }
105                 }
106
107                 else if ((!strcmp(cur->name, "domain")) && (cur->ns == ns))
108                         pdb_set_domain(u,
109                                                    xmlNodeListGetString(doc, cur->xmlChildrenNode,
110                                                                                                 1), PDB_SET);
111
112                 else if (!strcmp(cur->name, "fullname") && cur->ns == ns)
113                         pdb_set_fullname(u,
114                                                          xmlNodeListGetString(doc,
115                                                                                                   cur->xmlChildrenNode,
116                                                                                                   1), PDB_SET);
117
118                 else if (!strcmp(cur->name, "nt_username") && cur->ns == ns)
119                         pdb_set_nt_username(u,
120                                                                 xmlNodeListGetString(doc,
121                                                                                                          cur->xmlChildrenNode,
122                                                                                                          1), PDB_SET);
123
124                 else if (!strcmp(cur->name, "logon_script") && cur->ns == ns)
125                         pdb_set_logon_script(u,
126                                                                  xmlNodeListGetString(doc,
127                                                                                                           cur->xmlChildrenNode,
128                                                                                                           1), PDB_SET);
129
130                 else if (!strcmp(cur->name, "profile_path") && cur->ns == ns)
131                         pdb_set_profile_path(u,
132                                                                  xmlNodeListGetString(doc,
133                                                                                                           cur->xmlChildrenNode,
134                                                                                                           1), PDB_SET);
135
136                 else if (!strcmp(cur->name, "logon_time") && cur->ns == ns)
137                         pdb_set_logon_time(u,
138                                                            atol(xmlNodeListGetString
139                                                                         (doc, cur->xmlChildrenNode, 1)), PDB_SET);
140
141                 else if (!strcmp(cur->name, "logoff_time") && cur->ns == ns)
142                         pdb_set_logoff_time(u,
143                                                                 atol(xmlNodeListGetString
144                                                                          (doc, cur->xmlChildrenNode, 1)),
145                                                                 PDB_SET);
146
147                 else if (!strcmp(cur->name, "kickoff_time") && cur->ns == ns)
148                         pdb_set_kickoff_time(u,
149                                                                  atol(xmlNodeListGetString
150                                                                           (doc, cur->xmlChildrenNode, 1)),
151                                                                  PDB_SET);
152
153                 else if (!strcmp(cur->name, "logon_divs") && cur->ns == ns)
154                         pdb_set_logon_divs(u,
155                                                            atol(xmlNodeListGetString
156                                                                         (doc, cur->xmlChildrenNode, 1)), PDB_SET);
157
158                 else if (!strcmp(cur->name, "hours_len") && cur->ns == ns)
159                         pdb_set_hours_len(u,
160                                                           atol(xmlNodeListGetString
161                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
162
163                 else if (!strcmp(cur->name, "unknown_3") && cur->ns == ns)
164                         pdb_set_unknown_3(u,
165                                                           atol(xmlNodeListGetString
166                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
167
168                 else if (!strcmp(cur->name, "unknown_5") && cur->ns == ns)
169                         pdb_set_unknown_5(u,
170                                                           atol(xmlNodeListGetString
171                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
172
173                 else if (!strcmp(cur->name, "unknown_6") && cur->ns == ns)
174                         pdb_set_unknown_6(u,
175                                                           atol(xmlNodeListGetString
176                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
177
178                 else if (!strcmp(cur->name, "homedir") && cur->ns == ns)
179                         pdb_set_homedir(u,
180                                                         xmlNodeListGetString(doc, cur->xmlChildrenNode,
181                                                                                                  1), PDB_SET);
182
183                 else if (!strcmp(cur->name, "unknown_str") && cur->ns == ns)
184                         pdb_set_unknown_str(u,
185                                                                 xmlNodeListGetString(doc,
186                                                                                                          cur->xmlChildrenNode,
187                                                                                                          1), PDB_SET);
188
189                 else if (!strcmp(cur->name, "dir_drive") && cur->ns == ns)
190                         pdb_set_dir_drive(u,
191                                                           xmlNodeListGetString(doc,
192                                                                                                    cur->xmlChildrenNode,
193                                                                                                    1), PDB_SET);
194
195                 else if (!strcmp(cur->name, "munged_dial") && cur->ns == ns)
196                         pdb_set_munged_dial(u,
197                                                                 xmlNodeListGetString(doc,
198                                                                                                          cur->xmlChildrenNode,
199                                                                                                          1), PDB_SET);
200
201                 else if (!strcmp(cur->name, "acct_desc") && cur->ns == ns)
202                         pdb_set_acct_desc(u,
203                                                           xmlNodeListGetString(doc,
204                                                                                                    cur->xmlChildrenNode,
205                                                                                                    1), PDB_SET);
206
207                 else if (!strcmp(cur->name, "acct_ctrl") && cur->ns == ns)
208                         pdb_set_acct_ctrl(u,
209                                                           atol(xmlNodeListGetString
210                                                                    (doc, cur->xmlChildrenNode, 1)), PDB_SET);
211
212                 else if (!strcmp(cur->name, "workstations") && cur->ns == ns)
213                         pdb_set_workstations(u,
214                                                                  xmlNodeListGetString(doc,
215                                                                                                           cur->xmlChildrenNode,
216                                                                                                           1), PDB_SET);
217
218                 else if ((!strcmp(cur->name, "password")) && (cur->ns == ns)) {
219                         tmp = xmlGetProp(cur, "last_set");
220                         if (tmp)
221                                 pdb_set_pass_last_set_time(u, atol(tmp), PDB_SET);
222                         tmp = xmlGetProp(cur, "must_change");
223                         if (tmp)
224                                 pdb_set_pass_must_change_time(u, atol(tmp), PDB_SET);
225                         tmp = xmlGetProp(cur, "can_change");
226                         if (tmp)
227                                 pdb_set_pass_can_change_time(u, atol(tmp), PDB_SET);
228                         parsePass(doc, ns, cur, u);
229                 }
230
231                 else
232                         DEBUG(0, ("Unknown element %s\n", cur->name));
233                 cur = cur->next;
234         }
235
236         return True;
237 }
238
239 typedef struct pdb_xml {
240         char *location;
241         char written;
242         xmlDocPtr doc;
243         xmlNodePtr users;
244         xmlNodePtr pwent;
245         xmlNsPtr ns;
246 } pdb_xml;
247
248 static xmlNodePtr parseSambaXMLFile(struct pdb_xml *data)
249 {
250         xmlNodePtr cur;
251
252         data->doc = xmlParseFile(data->location);
253         if (data->doc == NULL)
254                 return NULL;
255
256         cur = xmlDocGetRootElement(data->doc);
257         if (!cur) {
258                 DEBUG(0, ("empty document\n"));
259                 xmlFreeDoc(data->doc);
260                 return NULL;
261         }
262         data->ns = xmlSearchNsByHref(data->doc, cur, XML_URL);
263         if (!data->ns) {
264                 DEBUG(0,
265                           ("document of the wrong type, samba user namespace not found\n"));
266                 xmlFreeDoc(data->doc);
267                 return NULL;
268         }
269         if (strcmp(cur->name, "samba")) {
270                 DEBUG(0, ("document of the wrong type, root node != samba"));
271                 xmlFreeDoc(data->doc);
272                 return NULL;
273         }
274
275         cur = cur->xmlChildrenNode;
276         while (cur && xmlIsBlankNode(cur)) {
277                 cur = cur->next;
278         }
279         if (!cur)
280                 return NULL;
281         if ((strcmp(cur->name, "users")) || (cur->ns != data->ns)) {
282                 DEBUG(0, ("document of the wrong type, was '%s', users expected",
283                                   cur->name));
284                 DEBUG(0, ("xmlDocDump follows\n"));
285                 xmlDocDump(stderr, data->doc);
286                 DEBUG(0, ("xmlDocDump finished\n"));
287                 xmlFreeDoc(data->doc);
288                 return NULL;
289         }
290         data->users = cur;
291         cur = cur->xmlChildrenNode;
292         return cur;
293 }
294
295 static NTSTATUS xmlsam_setsampwent(struct pdb_methods *methods, BOOL update)
296 {
297         pdb_xml *data;
298
299         if (!methods) {
300                 DEBUG(0, ("Invalid methods\n"));
301                 return NT_STATUS_INVALID_PARAMETER;
302         }
303         data = (pdb_xml *) methods->private_data;
304         if (!data) {
305                 DEBUG(0, ("Invalid pdb_xml_data\n"));
306                 return NT_STATUS_INVALID_PARAMETER;
307         }
308         data->pwent = parseSambaXMLFile(data);
309         if (!data->pwent)
310                 return NT_STATUS_UNSUCCESSFUL;
311         
312         return NT_STATUS_OK;
313 }
314
315 /***************************************************************
316   End enumeration of the passwd list.
317  ****************************************************************/
318
319 static void xmlsam_endsampwent(struct pdb_methods *methods)
320 {
321         pdb_xml *data;
322
323         if (!methods) {
324                 DEBUG(0, ("Invalid methods\n"));
325                 return;
326         }
327
328         data = (pdb_xml *) methods->private_data;
329
330         if (!data) {
331                 DEBUG(0, ("Invalid pdb_xml_data\n"));
332                 return;
333         }
334
335         xmlFreeDoc(data->doc);
336         data->doc = NULL;
337         data->pwent = NULL;
338 }
339
340 /*****************************************************************
341   Get one SAM_ACCOUNT from the list (next in line)
342  *****************************************************************/
343
344 static NTSTATUS xmlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
345 {
346         pdb_xml *data;
347
348         if (!methods) {
349                 DEBUG(0, ("Invalid methods\n"));
350                 return NT_STATUS_INVALID_PARAMETER;
351         }
352         data = (pdb_xml *) methods->private_data;
353
354         if (!data) {
355                 DEBUG(0, ("Invalid pdb_xml_data\n"));
356                 return NT_STATUS_INVALID_PARAMETER;
357         }
358
359         while (data->pwent) {
360                 if ((!strcmp(data->pwent->name, "user")) &&
361                         (data->pwent->ns == data->ns)) {
362
363                         parseUser(data->doc, data->ns, data->pwent, user);
364                         data->pwent = data->pwent->next;
365                         return NT_STATUS_OK;
366                 }
367                 data->pwent = data->pwent->next;
368         }
369         return NT_STATUS_UNSUCCESSFUL;
370 }
371
372 /***************************************************************************
373   Adds an existing SAM_ACCOUNT
374  ****************************************************************************/
375
376 static NTSTATUS xmlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * u)
377 {
378         pstring temp;
379         fstring sid_str;
380         xmlNodePtr cur, user, pass, root;
381         pdb_xml *data;
382
383         DEBUG(10, ("xmlsam_add_sam_account called!\n"));
384
385         if (!methods) {
386                 DEBUG(0, ("Invalid methods\n"));
387                 return NT_STATUS_INVALID_PARAMETER;
388         }
389
390         data = (pdb_xml *) methods->private_data;
391         if (!data) {
392                 DEBUG(0, ("Invalid pdb_xml_data\n"));
393                 return NT_STATUS_INVALID_PARAMETER;
394         }
395
396         /* Create a new document if we can't open the current one */
397         if (!parseSambaXMLFile(data)) {
398                 DEBUG(0, ("Can't load current XML file, creating a new one\n"));
399                 data->doc = xmlNewDoc(XML_DEFAULT_VERSION);
400                 root = xmlNewDocNode(data->doc, NULL, "samba", NULL);
401                 cur = xmlDocSetRootElement(data->doc, root);
402                 data->ns = xmlNewNs(root, XML_URL, "samba");
403                 data->users = xmlNewChild(root, data->ns, "users", NULL);
404         }
405
406         user = xmlNewChild(data->users, data->ns, "user", NULL);
407         xmlNewProp(user, "sid",
408                            sid_to_string(sid_str, pdb_get_user_sid(u)));
409         if (pdb_get_init_flags(u, PDB_UID) != PDB_DEFAULT)
410                 xmlNewProp(user, "uid", iota(pdb_get_uid(u)));
411
412         if (pdb_get_username(u) && strcmp(pdb_get_username(u), ""))
413                 xmlNewProp(user, "name", pdb_get_username(u));
414
415         cur = xmlNewChild(user, data->ns, "group", NULL);
416         
417         xmlNewProp(cur, "sid",
418                            sid_to_string(sid_str, pdb_get_group_sid(u)));
419         if (pdb_get_init_flags(u, PDB_GID) != PDB_DEFAULT)
420                 xmlNewProp(cur, "gid", iota(pdb_get_gid(u)));
421
422         if (pdb_get_init_flags(u, PDB_LOGONTIME) != PDB_DEFAULT)
423                 xmlNewChild(user, data->ns, "login_time",
424                                         iota(pdb_get_logon_time(u)));
425
426         if (pdb_get_init_flags(u, PDB_LOGOFFTIME) != PDB_DEFAULT)
427                 xmlNewChild(user, data->ns, "logoff_time",
428                                         iota(pdb_get_logoff_time(u)));
429
430         if (pdb_get_init_flags(u, PDB_KICKOFFTIME) != PDB_DEFAULT)
431                 xmlNewChild(user, data->ns, "kickoff_time",
432                                         iota(pdb_get_kickoff_time(u)));
433
434         if (pdb_get_domain(u) && strcmp(pdb_get_domain(u), ""))
435                 xmlNewChild(user, data->ns, "domain", pdb_get_domain(u));
436
437         if (pdb_get_nt_username(u) && strcmp(pdb_get_nt_username(u), ""))
438                 xmlNewChild(user, data->ns, "nt_username", pdb_get_nt_username(u));
439
440         if (pdb_get_fullname(u) && strcmp(pdb_get_fullname(u), ""))
441                 xmlNewChild(user, data->ns, "fullname", pdb_get_fullname(u));
442
443         if (pdb_get_homedir(u) && strcmp(pdb_get_homedir(u), ""))
444                 xmlNewChild(user, data->ns, "homedir", pdb_get_homedir(u));
445
446         if (pdb_get_dir_drive(u) && strcmp(pdb_get_dir_drive(u), ""))
447                 xmlNewChild(user, data->ns, "dir_drive", pdb_get_dir_drive(u));
448
449         if (pdb_get_logon_script(u) && strcmp(pdb_get_logon_script(u), ""))
450                 xmlNewChild(user, data->ns, "logon_script",
451                                         pdb_get_logon_script(u));
452
453         if (pdb_get_profile_path(u) && strcmp(pdb_get_profile_path(u), ""))
454                 xmlNewChild(user, data->ns, "profile_path",
455                                         pdb_get_profile_path(u));
456
457         if (pdb_get_acct_desc(u) && strcmp(pdb_get_acct_desc(u), ""))
458                 xmlNewChild(user, data->ns, "acct_desc", pdb_get_acct_desc(u));
459
460         if (pdb_get_workstations(u) && strcmp(pdb_get_workstations(u), ""))
461                 xmlNewChild(user, data->ns, "workstations",
462                                         pdb_get_workstations(u));
463
464         if (pdb_get_unknown_str(u) && strcmp(pdb_get_unknown_str(u), ""))
465                 xmlNewChild(user, data->ns, "unknown_str", pdb_get_unknown_str(u));
466
467         if (pdb_get_munged_dial(u) && strcmp(pdb_get_munged_dial(u), ""))
468                 xmlNewChild(user, data->ns, "munged_dial", pdb_get_munged_dial(u));
469
470
471         /* Password stuff */
472         pass = xmlNewChild(user, data->ns, "password", NULL);
473         if (pdb_get_pass_last_set_time(u))
474                 xmlNewProp(pass, "last_set", iota(pdb_get_pass_last_set_time(u)));
475         if (pdb_get_init_flags(u, PDB_CANCHANGETIME) != PDB_DEFAULT)
476                 xmlNewProp(pass, "can_change",
477                                    iota(pdb_get_pass_can_change_time(u)));
478
479         if (pdb_get_init_flags(u, PDB_MUSTCHANGETIME) != PDB_DEFAULT)
480                 xmlNewProp(pass, "must_change",
481                                    iota(pdb_get_pass_must_change_time(u)));
482
483
484         if (pdb_get_lanman_passwd(u)) {
485                 pdb_sethexpwd(temp, pdb_get_lanman_passwd(u),
486                                           pdb_get_acct_ctrl(u));
487                 cur = xmlNewChild(pass, data->ns, "crypt", temp);
488                 xmlNewProp(cur, "type", "lanman");
489         }
490
491         if (pdb_get_nt_passwd(u)) {
492                 pdb_sethexpwd(temp, pdb_get_nt_passwd(u), pdb_get_acct_ctrl(u));
493                 cur = xmlNewChild(pass, data->ns, "crypt", temp);
494                 xmlNewProp(cur, "type", "nt");
495         }
496
497         xmlNewChild(user, data->ns, "acct_ctrl", iota(pdb_get_acct_ctrl(u)));
498         xmlNewChild(user, data->ns, "unknown_3", iota(pdb_get_unknown_3(u)));
499
500         if (pdb_get_logon_divs(u))
501                 xmlNewChild(user, data->ns, "logon_divs",
502                                         iota(pdb_get_logon_divs(u)));
503
504         if (pdb_get_hours_len(u))
505                 xmlNewChild(user, data->ns, "hours_len",
506                                         iota(pdb_get_hours_len(u)));
507
508         xmlNewChild(user, data->ns, "unknown_5", iota(pdb_get_unknown_5(u)));
509         xmlNewChild(user, data->ns, "unknown_6", iota(pdb_get_unknown_6(u)));
510         xmlSaveFile(data->location, data->doc);
511
512         return NT_STATUS_OK;
513 }
514
515 NTSTATUS xmlsam_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method,
516                  const char *location)
517 {
518         NTSTATUS nt_status;
519         pdb_xml *data;
520
521         xmlsam_debug_level = debug_add_class("xmlsam");
522         if (xmlsam_debug_level == -1) {
523                 xmlsam_debug_level = DBGC_ALL;
524                 DEBUG(0, ("xmlsam: Couldn't register custom debugging class!\n"));
525         }
526
527         if (!pdb_context) {
528                 DEBUG(0, ("invalid pdb_methods specified\n"));
529                 return NT_STATUS_UNSUCCESSFUL;
530         }
531
532         if (!NT_STATUS_IS_OK
533                 (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
534                 return nt_status;
535         }
536
537         (*pdb_method)->name = "xmlsam";
538
539         (*pdb_method)->setsampwent = xmlsam_setsampwent;
540         (*pdb_method)->endsampwent = xmlsam_endsampwent;
541         (*pdb_method)->getsampwent = xmlsam_getsampwent;
542         (*pdb_method)->add_sam_account = xmlsam_add_sam_account;
543         (*pdb_method)->getsampwnam = NULL;
544         (*pdb_method)->getsampwsid = NULL;
545         (*pdb_method)->update_sam_account = NULL;
546         (*pdb_method)->delete_sam_account = NULL;
547         (*pdb_method)->getgrsid = NULL;
548         (*pdb_method)->getgrgid = NULL;
549         (*pdb_method)->getgrnam = NULL;
550         (*pdb_method)->add_group_mapping_entry = NULL;
551         (*pdb_method)->update_group_mapping_entry = NULL;
552         (*pdb_method)->delete_group_mapping_entry = NULL;
553         (*pdb_method)->enum_group_mapping = NULL;
554
555         data = talloc(pdb_context->mem_ctx, sizeof(pdb_xml));
556         data->location =
557                 (location ? talloc_strdup(pdb_context->mem_ctx, location) : "-");
558         data->pwent = NULL;
559         data->written = 0;
560         (*pdb_method)->private_data = data;
561
562         LIBXML_TEST_VERSION xmlKeepBlanksDefault(0);
563
564         return NT_STATUS_OK;
565 }
566
567 int pdb_xml_init(void) 
568 {
569         return smb_register_passdb("xml", xmlsam_init, PASSDB_INTERFACE_VERSION);
570 }