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/~jelmer/sambapdb.dtd"
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
)
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
);