3 * XML password backend for samba
4 * Copyright (C) Jelmer Vernooij 2002
5 * Some parts based on the libxml gjobread example by Daniel Veillard
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)
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
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.
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
31 #define XML_URL "http://samba.org/samba/DTD/passdb"
35 #include <libxml/xmlmemory.h>
36 #include <libxml/parser.h>
38 static int xmlsam_debug_level = DBGC_ALL;
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)
50 if(!content) return xmlNewChild(prnt, ns, name, NULL);
53 if(push_utf8_allocate(&string_utf8,content) == (size_t)-1)
56 ret = xmlNewTextChild(prnt, ns, name, string_utf8);
58 SAFE_FREE(string_utf8);
64 static char * iota(int a) {
67 snprintf(tmp, 9, "%d", a);
71 static BOOL parsePass(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
75 cur = cur->xmlChildrenNode;
77 if (strcmp(cur->name, "crypt"))
78 DEBUG(0, ("Unknown element %s\n", cur->name));
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);
92 ("Unknown crypt type: %s\n",
93 xmlGetProp(cur, "type")));
100 static BOOL parseUser(xmlDocPtr doc, xmlNsPtr ns, xmlNodePtr cur, SAM_ACCOUNT * u)
105 tmp = xmlGetProp(cur, "sid");
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");
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))
124 xmlNodeListGetString(doc, cur->xmlChildrenNode,
127 else if (!strcmp(cur->name, "fullname") && cur->ns == ns)
129 xmlNodeListGetString(doc,
130 cur->xmlChildrenNode,
133 else if (!strcmp(cur->name, "nt_username") && cur->ns == ns)
134 pdb_set_nt_username(u,
135 xmlNodeListGetString(doc,
136 cur->xmlChildrenNode,
139 else if (!strcmp(cur->name, "logon_script") && cur->ns == ns)
140 pdb_set_logon_script(u,
141 xmlNodeListGetString(doc,
142 cur->xmlChildrenNode,
145 else if (!strcmp(cur->name, "profile_path") && cur->ns == ns)
146 pdb_set_profile_path(u,
147 xmlNodeListGetString(doc,
148 cur->xmlChildrenNode,
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)),
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)),
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)
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)
190 atol(xmlNodeListGetString
191 (doc, cur->xmlChildrenNode, 1)), PDB_SET);
193 else if (!strcmp(cur->name, "homedir") && cur->ns == ns)
195 xmlNodeListGetString(doc, cur->xmlChildrenNode,
198 else if (!strcmp(cur->name, "unknown_str") && cur->ns == ns)
199 pdb_set_unknown_str(u,
200 xmlNodeListGetString(doc,
201 cur->xmlChildrenNode,
204 else if (!strcmp(cur->name, "dir_drive") && cur->ns == ns)
206 xmlNodeListGetString(doc,
207 cur->xmlChildrenNode,
210 else if (!strcmp(cur->name, "munged_dial") && cur->ns == ns)
211 pdb_set_munged_dial(u,
212 xmlNodeListGetString(doc,
213 cur->xmlChildrenNode,
216 else if (!strcmp(cur->name, "acct_desc") && cur->ns == ns)
218 xmlNodeListGetString(doc,
219 cur->xmlChildrenNode,
222 else if (!strcmp(cur->name, "acct_ctrl") && cur->ns == ns)
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,
233 else if ((!strcmp(cur->name, "password")) && (cur->ns == ns)) {
234 tmp = xmlGetProp(cur, "last_set");
236 pdb_set_pass_last_set_time(u, atol(tmp), PDB_SET);
237 tmp = xmlGetProp(cur, "must_change");
239 pdb_set_pass_must_change_time(u, atol(tmp), PDB_SET);
240 tmp = xmlGetProp(cur, "can_change");
242 pdb_set_pass_can_change_time(u, atol(tmp), PDB_SET);
243 parsePass(doc, ns, cur, u);
247 DEBUG(0, ("Unknown element %s\n", cur->name));
254 typedef struct pdb_xml {
263 static xmlNodePtr parseSambaXMLFile(struct pdb_xml *data)
267 data->doc = xmlParseFile(data->location);
268 if (data->doc == NULL)
271 cur = xmlDocGetRootElement(data->doc);
273 DEBUG(0, ("empty document\n"));
274 xmlFreeDoc(data->doc);
277 data->ns = xmlSearchNsByHref(data->doc, cur, XML_URL);
280 ("document of the wrong type, samba user namespace not found\n"));
281 xmlFreeDoc(data->doc);
284 if (strcmp(cur->name, "samba")) {
285 DEBUG(0, ("document of the wrong type, root node != samba"));
286 xmlFreeDoc(data->doc);
290 cur = cur->xmlChildrenNode;
291 while (cur && xmlIsBlankNode(cur)) {
296 if ((strcmp(cur->name, "users")) || (cur->ns != data->ns)) {
297 DEBUG(0, ("document of the wrong type, was '%s', users expected",
299 DEBUG(0, ("xmlDocDump follows\n"));
300 xmlDocDump(stderr, data->doc);
301 DEBUG(0, ("xmlDocDump finished\n"));
302 xmlFreeDoc(data->doc);
306 cur = cur->xmlChildrenNode;
310 static NTSTATUS xmlsam_setsampwent(struct pdb_methods *methods, BOOL update, uint16 acb_mask)
315 DEBUG(0, ("Invalid methods\n"));
316 return NT_STATUS_INVALID_PARAMETER;
318 data = (pdb_xml *) methods->private_data;
320 DEBUG(0, ("Invalid pdb_xml_data\n"));
321 return NT_STATUS_INVALID_PARAMETER;
323 data->pwent = parseSambaXMLFile(data);
325 return NT_STATUS_UNSUCCESSFUL;
330 /***************************************************************
331 End enumeration of the passwd list.
332 ****************************************************************/
334 static void xmlsam_endsampwent(struct pdb_methods *methods)
339 DEBUG(0, ("Invalid methods\n"));
343 data = (pdb_xml *) methods->private_data;
346 DEBUG(0, ("Invalid pdb_xml_data\n"));
350 xmlFreeDoc(data->doc);
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)
364 DEBUG(0, ("Invalid methods\n"));
365 return NT_STATUS_INVALID_PARAMETER;
367 data = (pdb_xml *) methods->private_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;
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)
395 xmlNodePtr cur, user, pass, root;
398 DEBUG(10, ("xmlsam_add_sam_account called!\n"));
401 DEBUG(0, ("Invalid methods\n"));
402 return NT_STATUS_INVALID_PARAMETER;
405 data = (pdb_xml *) methods->private_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));
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);
526 static NTSTATUS xmlsam_init(PDB_CONTEXT * pdb_context, PDB_METHODS ** pdb_method,
527 const char *location)
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"));
539 DEBUG(0, ("invalid pdb_methods specified\n"));
540 return NT_STATUS_UNSUCCESSFUL;
544 (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
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"));
570 (*pdb_method)->private_data = data;
572 LIBXML_TEST_VERSION xmlKeepBlanksDefault(0);
577 NTSTATUS pdb_xml_init(void)
579 return smb_register_passdb(PASSDB_INTERFACE_VERSION, "xml", xmlsam_init);