import HEAD into svn+ssh://svn.samba.org/home/svn/samba/trunk
[metze/old/v3-2-winbind-ndr.git] / source / passdb / pdb_mysql.c
1 /*
2  * MySQL password backend for samba
3  * Copyright (C) Jelmer Vernooij 2002-2004
4  * 
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  * 
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  * 
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 675
17  * Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include "includes.h"
21 #include <mysql/mysql.h>
22
23 #define CONFIG_HOST_DEFAULT                             "localhost"
24 #define CONFIG_USER_DEFAULT                             "samba"
25 #define CONFIG_PASS_DEFAULT                             ""
26 #define CONFIG_PORT_DEFAULT                             "3306"
27 #define CONFIG_DB_DEFAULT                               "samba"
28
29 static int mysqlsam_debug_level = DBGC_ALL;
30
31 #undef DBGC_CLASS
32 #define DBGC_CLASS mysqlsam_debug_level
33
34 typedef struct pdb_mysql_data {
35         MYSQL *handle;
36         MYSQL_RES *pwent;
37         const char *location;
38 } pdb_mysql_data;
39
40 #define SET_DATA(data,methods) { \
41         if(!methods){ \
42                 DEBUG(0, ("invalid methods!\n")); \
43                         return NT_STATUS_INVALID_PARAMETER; \
44         } \
45         data = (struct pdb_mysql_data *)methods->private_data; \
46                 if(!data || !(data->handle)){ \
47                         DEBUG(0, ("invalid handle!\n")); \
48                                 return NT_STATUS_INVALID_HANDLE; \
49                 } \
50 }
51
52 #define config_value( data, name, default_value ) \
53   lp_parm_const_string( GLOBAL_SECTION_SNUM, (data)->location, name, default_value )
54
55 static long xatol(const char *d)
56 {
57         if(!d) return 0;
58         return atol(d);
59 }
60
61 static NTSTATUS row_to_sam_account(MYSQL_RES * r, SAM_ACCOUNT * u)
62 {
63         MYSQL_ROW row;
64         pstring temp;
65         unsigned int num_fields;
66         DOM_SID sid;
67
68         num_fields = mysql_num_fields(r);
69         row = mysql_fetch_row(r);
70         if (!row)
71                 return NT_STATUS_INVALID_PARAMETER;
72
73         pdb_set_logon_time(u, xatol(row[0]), PDB_SET);
74         pdb_set_logoff_time(u, xatol(row[1]), PDB_SET);
75         pdb_set_kickoff_time(u, xatol(row[2]), PDB_SET);
76         pdb_set_pass_last_set_time(u, xatol(row[3]), PDB_SET);
77         pdb_set_pass_can_change_time(u, xatol(row[4]), PDB_SET);
78         pdb_set_pass_must_change_time(u, xatol(row[5]), PDB_SET);
79         pdb_set_username(u, row[6], PDB_SET);
80         pdb_set_domain(u, row[7], PDB_SET);
81         pdb_set_nt_username(u, row[8], PDB_SET);
82         pdb_set_fullname(u, row[9], PDB_SET);
83         pdb_set_homedir(u, row[10], PDB_SET);
84         pdb_set_dir_drive(u, row[11], PDB_SET);
85         pdb_set_logon_script(u, row[12], PDB_SET);
86         pdb_set_profile_path(u, row[13], PDB_SET);
87         pdb_set_acct_desc(u, row[14], PDB_SET);
88         pdb_set_workstations(u, row[15], PDB_SET);
89         pdb_set_unknown_str(u, row[16], PDB_SET);
90         pdb_set_munged_dial(u, row[17], PDB_SET);
91
92         if(!row[18] || !string_to_sid(&sid, row[18])) {
93                 DEBUG(0,("No user SID retrieved from database!\n"));
94         } else {
95                 pdb_set_user_sid(u, &sid, PDB_SET);
96         }
97
98         if(row[19]) {
99                 string_to_sid(&sid, row[19]);
100                 pdb_set_group_sid(u, &sid, PDB_SET);
101         }
102
103         if (pdb_gethexpwd(row[20], temp))
104                 pdb_set_lanman_passwd(u, temp, PDB_SET);
105         if (pdb_gethexpwd(row[21], temp))
106                 pdb_set_nt_passwd(u, temp, PDB_SET);
107
108         /* Only use plaintext password storage when lanman and nt are
109          * NOT used */
110         if (!row[20] || !row[21])
111                 pdb_set_plaintext_passwd(u, row[22]);
112
113         pdb_set_acct_ctrl(u, xatol(row[23]), PDB_SET);
114         pdb_set_logon_divs(u, xatol(row[25]), PDB_SET);
115         pdb_set_hours_len(u, xatol(row[26]), PDB_SET);
116         pdb_set_bad_password_count(u, xatol(row[27]), PDB_SET);
117         pdb_set_logon_count(u, xatol(row[28]), PDB_SET);
118         pdb_set_unknown_6(u, xatol(row[29]), PDB_SET);
119
120         return NT_STATUS_OK;
121 }
122
123 static NTSTATUS mysqlsam_setsampwent(struct pdb_methods *methods, BOOL update)
124 {
125         struct pdb_mysql_data *data =
126                 (struct pdb_mysql_data *) methods->private_data;
127         char *query;
128         int ret;
129
130         if (!data || !(data->handle)) {
131                 DEBUG(0, ("invalid handle!\n"));
132                 return NT_STATUS_INVALID_HANDLE;
133         }
134
135         query = sql_account_query_select(data->location, update, SQL_SEARCH_NONE, NULL);
136
137         ret = mysql_query(data->handle, query);
138         SAFE_FREE(query);
139
140         if (ret) {
141                 DEBUG(0,
142                            ("Error executing MySQL query %s\n", mysql_error(data->handle)));
143                 return NT_STATUS_UNSUCCESSFUL;
144         }
145
146         data->pwent = mysql_store_result(data->handle);
147
148         if (data->pwent == NULL) {
149                 DEBUG(0,
150                         ("Error storing results: %s\n", mysql_error(data->handle)));
151                 return NT_STATUS_UNSUCCESSFUL;
152         }
153         
154         DEBUG(5,
155                 ("mysqlsam_setsampwent succeeded(%llu results)!\n",
156                                 mysql_num_rows(data->pwent)));
157         
158         return NT_STATUS_OK;
159 }
160
161 /***************************************************************
162   End enumeration of the passwd list.
163  ****************************************************************/
164
165 static void mysqlsam_endsampwent(struct pdb_methods *methods)
166 {
167         struct pdb_mysql_data *data =
168                 (struct pdb_mysql_data *) methods->private_data;
169
170         if (data == NULL) {
171                 DEBUG(0, ("invalid handle!\n"));
172                 return;
173         }
174
175         if (data->pwent != NULL)
176                 mysql_free_result(data->pwent);
177
178         data->pwent = NULL;
179
180         DEBUG(5, ("mysql_endsampwent called\n"));
181 }
182
183 /*****************************************************************
184   Get one SAM_ACCOUNT from the list (next in line)
185  *****************************************************************/
186
187 static NTSTATUS mysqlsam_getsampwent(struct pdb_methods *methods, SAM_ACCOUNT * user)
188 {
189         struct pdb_mysql_data *data;
190
191         SET_DATA(data, methods);
192
193         if (data->pwent == NULL) {
194                 DEBUG(0, ("invalid pwent\n"));
195                 return NT_STATUS_INVALID_PARAMETER;
196         }
197
198         return row_to_sam_account(data->pwent, user);
199 }
200
201 static NTSTATUS mysqlsam_select_by_field(struct pdb_methods * methods, SAM_ACCOUNT * user,
202                                                  enum sql_search_field field, const char *sname)
203 {
204         char *esc_sname;
205         char *query;
206         NTSTATUS ret;
207         MYSQL_RES *res;
208         int mysql_ret;
209         struct pdb_mysql_data *data;
210         char *tmp_sname;
211
212         SET_DATA(data, methods);
213
214         esc_sname = malloc(strlen(sname) * 2 + 1);
215         if (!esc_sname) {
216                 return NT_STATUS_NO_MEMORY; 
217         }
218
219         tmp_sname = smb_xstrdup(sname);
220         
221         /* Escape sname */
222         mysql_real_escape_string(data->handle, esc_sname, tmp_sname,
223                                                          strlen(tmp_sname));
224
225         SAFE_FREE(tmp_sname);
226
227         if (user == NULL) {
228                 DEBUG(0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n"));
229                 SAFE_FREE(esc_sname);
230                 return NT_STATUS_INVALID_PARAMETER;
231         }
232
233         query = sql_account_query_select(data->location, True, field, esc_sname);
234
235         SAFE_FREE(esc_sname);
236
237         DEBUG(5, ("Executing query %s\n", query));
238         
239         mysql_ret = mysql_query(data->handle, query);
240         
241         SAFE_FREE(query);
242         
243         if (mysql_ret) {
244                 DEBUG(0,
245                         ("Error while executing MySQL query %s\n", 
246                                 mysql_error(data->handle)));
247                 return NT_STATUS_UNSUCCESSFUL;
248         }
249         
250         res = mysql_store_result(data->handle);
251         if (res == NULL) {
252                 DEBUG(0,
253                         ("Error storing results: %s\n", mysql_error(data->handle)));
254                 return NT_STATUS_UNSUCCESSFUL;
255         }
256         
257         ret = row_to_sam_account(res, user);
258         mysql_free_result(res);
259
260         return ret;
261 }
262
263 /******************************************************************
264   Lookup a name in the SAM database
265  ******************************************************************/
266
267 static NTSTATUS mysqlsam_getsampwnam(struct pdb_methods *methods, SAM_ACCOUNT * user,
268                                          const char *sname)
269 {
270         struct pdb_mysql_data *data;
271
272         SET_DATA(data, methods);
273
274         if (!sname) {
275                 DEBUG(0, ("invalid name specified"));
276                 return NT_STATUS_INVALID_PARAMETER;
277         }
278
279         return mysqlsam_select_by_field(methods, user,
280                         SQL_SEARCH_USER_NAME, sname);
281 }
282
283
284 /***************************************************************************
285   Search by sid
286  **************************************************************************/
287
288 static NTSTATUS mysqlsam_getsampwsid(struct pdb_methods *methods, SAM_ACCOUNT * user,
289                                          const DOM_SID * sid)
290 {
291         struct pdb_mysql_data *data;
292         fstring sid_str;
293
294         SET_DATA(data, methods);
295
296         sid_to_string(sid_str, sid);
297
298         return mysqlsam_select_by_field(methods, user, SQL_SEARCH_USER_SID, sid_str);
299 }
300
301 /***************************************************************************
302   Delete a SAM_ACCOUNT
303  ****************************************************************************/
304
305 static NTSTATUS mysqlsam_delete_sam_account(struct pdb_methods *methods,
306                                                         SAM_ACCOUNT * sam_pass)
307 {
308         const char *sname = pdb_get_username(sam_pass);
309         char *esc;
310         char *query;
311         int ret;
312         struct pdb_mysql_data *data;
313         char *tmp_sname;
314
315         SET_DATA(data, methods);
316
317         if (!methods) {
318                 DEBUG(0, ("invalid methods!\n"));
319                 return NT_STATUS_INVALID_PARAMETER;
320         }
321
322         data = (struct pdb_mysql_data *) methods->private_data;
323         if (!data || !(data->handle)) {
324                 DEBUG(0, ("invalid handle!\n"));
325                 return NT_STATUS_INVALID_HANDLE;
326         }
327
328         if (!sname) {
329                 DEBUG(0, ("invalid name specified\n"));
330                 return NT_STATUS_INVALID_PARAMETER;
331         }
332
333         /* Escape sname */
334         esc = malloc(strlen(sname) * 2 + 1);
335         if (!esc) {
336                 DEBUG(0, ("Can't allocate memory to store escaped name\n"));
337                 return NT_STATUS_NO_MEMORY;
338         }
339         
340         tmp_sname = smb_xstrdup(sname);
341         
342         mysql_real_escape_string(data->handle, esc, tmp_sname,
343                                                          strlen(tmp_sname));
344
345         SAFE_FREE(tmp_sname);
346
347         query = sql_account_query_delete(data->location, esc);
348
349         SAFE_FREE(esc);
350
351         ret = mysql_query(data->handle, query);
352
353         SAFE_FREE(query);
354
355         if (ret) {
356                 DEBUG(0,
357                           ("Error while executing query: %s\n",
358                            mysql_error(data->handle)));
359                 return NT_STATUS_UNSUCCESSFUL;
360         }
361
362         DEBUG(5, ("User '%s' deleted\n", sname));
363         return NT_STATUS_OK;
364 }
365
366 static NTSTATUS mysqlsam_replace_sam_account(struct pdb_methods *methods,
367                                                          const SAM_ACCOUNT * newpwd, char isupdate)
368 {
369         struct pdb_mysql_data *data;
370         char *query;
371
372         if (!methods) {
373                 DEBUG(0, ("invalid methods!\n"));
374                 return NT_STATUS_INVALID_PARAMETER;
375         }
376
377         data = (struct pdb_mysql_data *) methods->private_data;
378
379         if (data == NULL || data->handle == NULL) {
380                 DEBUG(0, ("invalid handle!\n"));
381                 return NT_STATUS_INVALID_HANDLE;
382         }
383
384         query = sql_account_query_update(data->location, newpwd, isupdate);
385         
386         /* Execute the query */
387         if (mysql_query(data->handle, query)) {
388                 DEBUG(0,
389                           ("Error executing %s, %s\n", query,
390                            mysql_error(data->handle)));
391                 return NT_STATUS_INVALID_PARAMETER;
392         }
393         SAFE_FREE(query);
394
395         return NT_STATUS_OK;
396 }
397
398 static NTSTATUS mysqlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * newpwd)
399 {
400         return mysqlsam_replace_sam_account(methods, newpwd, 0);
401 }
402
403 static NTSTATUS mysqlsam_update_sam_account(struct pdb_methods *methods,
404                                                         SAM_ACCOUNT * newpwd)
405 {
406         return mysqlsam_replace_sam_account(methods, newpwd, 1);
407 }
408
409 static NTSTATUS mysqlsam_init(struct pdb_context * pdb_context, struct pdb_methods ** pdb_method,
410                  const char *location)
411 {
412         NTSTATUS nt_status;
413         struct pdb_mysql_data *data;
414
415         mysqlsam_debug_level = debug_add_class("mysqlsam");
416         if (mysqlsam_debug_level == -1) {
417                 mysqlsam_debug_level = DBGC_ALL;
418                 DEBUG(0,
419                           ("mysqlsam: Couldn't register custom debugging class!\n"));
420         }
421
422
423         if (!pdb_context) {
424                 DEBUG(0, ("invalid pdb_methods specified\n"));
425                 return NT_STATUS_UNSUCCESSFUL;
426         }
427
428         if (!NT_STATUS_IS_OK
429                 (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
430                 return nt_status;
431         }
432
433         (*pdb_method)->name = "mysqlsam";
434
435         (*pdb_method)->setsampwent = mysqlsam_setsampwent;
436         (*pdb_method)->endsampwent = mysqlsam_endsampwent;
437         (*pdb_method)->getsampwent = mysqlsam_getsampwent;
438         (*pdb_method)->getsampwnam = mysqlsam_getsampwnam;
439         (*pdb_method)->getsampwsid = mysqlsam_getsampwsid;
440         (*pdb_method)->add_sam_account = mysqlsam_add_sam_account;
441         (*pdb_method)->update_sam_account = mysqlsam_update_sam_account;
442         (*pdb_method)->delete_sam_account = mysqlsam_delete_sam_account;
443
444         data = talloc(pdb_context->mem_ctx, sizeof(struct pdb_mysql_data));
445         (*pdb_method)->private_data = data;
446         data->handle = NULL;
447         data->pwent = NULL;
448
449         if (!location) {
450                 DEBUG(0, ("No identifier specified. Check the Samba HOWTO Collection for details\n"));
451                 return NT_STATUS_INVALID_PARAMETER;
452         }
453
454         data->location = smb_xstrdup(location);
455
456         DEBUG(1,
457                   ("Connecting to database server, host: %s, user: %s, password: %s, database: %s, port: %ld\n",
458                    config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
459                    config_value(data, "mysql user", CONFIG_USER_DEFAULT),
460                    config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
461                    config_value(data, "mysql database", CONFIG_DB_DEFAULT),
462                    xatol(config_value(data, "mysql port", CONFIG_PORT_DEFAULT))));
463
464         /* Do the mysql initialization */
465         data->handle = mysql_init(NULL);
466         if (!data->handle) {
467                 DEBUG(0, ("Failed to connect to server\n"));
468                 return NT_STATUS_UNSUCCESSFUL;
469         }
470
471         if(!sql_account_config_valid(data->location)) {
472                 return NT_STATUS_INVALID_PARAMETER;
473         }
474         
475         /* Process correct entry in $HOME/.my.conf */
476         if (!mysql_real_connect(data->handle,
477                         config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
478                         config_value(data, "mysql user", CONFIG_USER_DEFAULT),
479                         config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
480                         config_value(data, "mysql database", CONFIG_DB_DEFAULT),
481                         xatol(config_value (data, "mysql port", CONFIG_PORT_DEFAULT)), 
482                         NULL, 0)) {
483                 DEBUG(0,
484                           ("Failed to connect to mysql database: error: %s\n",
485                            mysql_error(data->handle)));
486                 return NT_STATUS_UNSUCCESSFUL;
487         }
488         
489         DEBUG(5, ("Connected to mysql db\n"));
490
491         return NT_STATUS_OK;
492 }
493
494 NTSTATUS pdb_mysql_init(void) 
495 {
496         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "mysql", mysqlsam_init);
497 }