r2565: syncing up for 3.0.8pre1
[Samba.git] / source / passdb / pdb_xml.c
blob6eff57d365e350016de209fe17a8b6913968d18b
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.
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.
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.
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!
31 #define XML_URL "http://samba.org/~jelmer/sambapdb.dtd"
33 #include "includes.h"
35 #include <libxml/xmlmemory.h>
36 #include <libxml/parser.h>
38 static int xmlsam_debug_level = DBGC_ALL;
40 #undef DBGC_CLASS
41 #define DBGC_CLASS xmlsam_debug_level
44 /* Helper utilities for charset conversion */
45 static xmlNodePtr smbXmlNewChild(xmlNodePtr prnt, xmlNsPtr ns, const xmlChar *name, const char *content)
47 char *string_utf8;
48 xmlNodePtr ret;
50 if(!content) return xmlNewChild(prnt, ns, name, NULL);
53 if(push_utf8_allocate(&string_utf8,content) == (size_t)-1)
54 return NULL;
56 ret = xmlNewTextChild(prnt, ns, name, string_utf8);
58 SAFE_FREE(string_utf8);
60 return ret;
64 static char * iota(int a) {
65 static char tmp[10];
67 snprintf(tmp, 9, "%d", a);
68 return tmp;
71 static BOOL parsePass(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
73 pstring temp;
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")
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")
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")));
95 cur = cur->next;
97 return True;
100 static BOOL parseUser(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
102 char *tmp;
103 DOM_SID sid;
105 tmp = xmlGetProp(cur, "sid");
106 if (tmp){
107 string_to_sid(&sid, tmp);
108 pdb_set_user_sid(u, &sid, PDB_SET);
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);
122 else if ((!strcmp(cur->name, "domain")) && (cur->ns == ns))
123 pdb_set_domain(u,
124 xmlNodeListGetString(doc, cur->xmlChildrenNode,
125 1), PDB_SET);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
193 else if (!strcmp(cur->name, "homedir") && cur->ns == ns)
194 pdb_set_homedir(u,
195 xmlNodeListGetString(doc, cur->xmlChildrenNode,
196 1), PDB_SET);
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);
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);
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);
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);
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);
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);
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);
246 else
247 DEBUG(0, ("Unknown element %s\n", cur->name));
248 cur = cur->next;
251 return True;
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;
263 static xmlNodePtr parseSambaXMLFile(struct pdb_xml *data)
265 xmlNodePtr cur;
267 data->doc = xmlParseFile(data->location);
268 if (data->doc == NULL)
269 return NULL;
271 cur = xmlDocGetRootElement(data->doc);
272 if (!cur) {
273 DEBUG(0, ("empty document\n"));
274 xmlFreeDoc(data->doc);
275 return NULL;
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;
284 if (strcmp(cur->name, "samba")) {
285 DEBUG(0, ("document of the wrong type, root node != samba"));
286 xmlFreeDoc(data->doc);
287 return NULL;
290 cur = cur->xmlChildrenNode;
291 while (cur && xmlIsBlankNode(cur)) {
292 cur = cur->next;
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;
305 data->users = cur;
306 cur = cur->xmlChildrenNode;
307 return cur;
310 static NTSTATUS xmlsam_setsampwent(struct pdb_methods *methods, BOOL update)
312 pdb_xml *data;
314 if (!methods) {
315 DEBUG(0, ("Invalid methods\n"));
316 return NT_STATUS_INVALID_PARAMETER;
318 data = (pdb_xml *) methods->private_data;
319 if (!data) {
320 DEBUG(0, ("Invalid pdb_xml_data\n"));
321 return NT_STATUS_INVALID_PARAMETER;
323 data->pwent = parseSambaXMLFile(data);
324 if (!data->pwent)
325 return NT_STATUS_UNSUCCESSFUL;
327 return NT_STATUS_OK;
330 /***************************************************************
331 End enumeration of the passwd list.
332 ****************************************************************/
334 static void xmlsam_endsampwent(struct pdb_methods *methods)
336 pdb_xml *data;
338 if (!methods) {
339 DEBUG(0, ("Invalid methods\n"));
340 return;
343 data = (pdb_xml *) methods->private_data;
345 if (!data) {
346 DEBUG(0, ("Invalid pdb_xml_data\n"));
347 return;
350 xmlFreeDoc(data->doc);
351 data->doc = NULL;
352 data->pwent = NULL;
355 /*****************************************************************
356 Get one SAM_ACCOUNT from the list (next in line)
357 *****************************************************************/
359 static NTSTATUS xmlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
361 pdb_xml *data;
363 if (!methods) {
364 DEBUG(0, ("Invalid methods\n"));
365 return NT_STATUS_INVALID_PARAMETER;
367 data = (pdb_xml *) methods->private_data;
369 if (!data) {
370 DEBUG(0, ("Invalid pdb_xml_data\n"));
371 return NT_STATUS_INVALID_PARAMETER;
374 while (data->pwent) {
375 if ((!strcmp(data->pwent->name, "user")) &&
376 (data->pwent->ns == data->ns)) {
378 parseUser(data->doc, data->ns, data->pwent, user);
379 data->pwent = data->pwent->next;
380 return NT_STATUS_OK;
382 data->pwent = data->pwent->next;
384 return NT_STATUS_UNSUCCESSFUL;
387 /***************************************************************************
388 Adds an existing SAM_ACCOUNT
389 ****************************************************************************/
391 static NTSTATUS xmlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * u)
393 pstring temp;
394 fstring sid_str;
395 xmlNodePtr cur, user, pass, root;
396 pdb_xml *data;
398 DEBUG(10, ("xmlsam_add_sam_account called!\n"));
400 if (!methods) {
401 DEBUG(0, ("Invalid methods\n"));
402 return NT_STATUS_INVALID_PARAMETER;
405 data = (pdb_xml *) methods->private_data;
406 if (!data) {
407 DEBUG(0, ("Invalid pdb_xml_data\n"));
408 return NT_STATUS_INVALID_PARAMETER;
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);
421 user = smbXmlNewChild(data->users, data->ns, "user", NULL);
422 xmlNewProp(user, "sid",
423 sid_to_string(sid_str, pdb_get_user_sid(u)));
425 if (pdb_get_username(u) && strcmp(pdb_get_username(u), ""))
426 xmlNewProp(user, "name", pdb_get_username(u));
428 cur = smbXmlNewChild(user, data->ns, "group", NULL);
430 xmlNewProp(cur, "sid",
431 sid_to_string(sid_str, pdb_get_group_sid(u)));
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)));
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)));
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)));
445 if (pdb_get_domain(u) && strcmp(pdb_get_domain(u), ""))
446 smbXmlNewChild(user, data->ns, "domain", pdb_get_domain(u));
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));
451 if (pdb_get_fullname(u) && strcmp(pdb_get_fullname(u), ""))
452 smbXmlNewChild(user, data->ns, "fullname", pdb_get_fullname(u));
454 if (pdb_get_homedir(u) && strcmp(pdb_get_homedir(u), ""))
455 smbXmlNewChild(user, data->ns, "homedir", pdb_get_homedir(u));
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));
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));
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));
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));
471 if (pdb_get_workstations(u) && strcmp(pdb_get_workstations(u), ""))
472 smbXmlNewChild(user, data->ns, "workstations",
473 pdb_get_workstations(u));
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));
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));
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)));
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)));
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");
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");
508 smbXmlNewChild(user, data->ns, "acct_ctrl", iota(pdb_get_acct_ctrl(u)));
510 if (pdb_get_logon_divs(u))
511 smbXmlNewChild(user, data->ns, "logon_divs",
512 iota(pdb_get_logon_divs(u)));
514 if (pdb_get_hours_len(u))
515 smbXmlNewChild(user, data->ns, "hours_len",
516 iota(pdb_get_hours_len(u)));
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);
523 return NT_STATUS_OK;
526 static NTSTATUS xmlsam_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method,
527 const char *location)
529 NTSTATUS nt_status;
530 pdb_xml *data;
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"));
538 if (!pdb_context) {
539 DEBUG(0, ("invalid pdb_methods specified\n"));
540 return NT_STATUS_UNSUCCESSFUL;
543 if (!NT_STATUS_IS_OK
544 (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
545 return nt_status;
548 (*pdb_method)->name = "xmlsam";
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;
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;
572 LIBXML_TEST_VERSION xmlKeepBlanksDefault(0);
574 return NT_STATUS_OK;
577 NTSTATUS pdb_xml_init(void)
579 return smb_register_passdb(PASSDB_INTERFACE_VERSION, "xml", xmlsam_init);