Initial import
[samba] / 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[24]), PDB_SET);
115         pdb_set_hours_len(u, xatol(row[25]), PDB_SET);
116         pdb_set_bad_password_count(u, xatol(row[26]), PDB_SET);
117         pdb_set_logon_count(u, xatol(row[27]), PDB_SET);
118         pdb_set_unknown_6(u, xatol(row[28]), PDB_SET);
119
120         return NT_STATUS_OK;
121 }
122
123 static NTSTATUS mysqlsam_setsampwent(struct pdb_methods *methods, BOOL update, uint16 acb_mask)
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(NULL, data->location, update, SQL_SEARCH_NONE, NULL);
136
137         ret = mysql_query(data->handle, query);
138         talloc_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         TALLOC_CTX *mem_ctx = talloc_init("mysqlsam_select_by_field");
212
213         SET_DATA(data, methods);
214
215         esc_sname = talloc_array(mem_ctx, char, strlen(sname) * 2 + 1);
216         if (!esc_sname) {
217                 talloc_free(mem_ctx);
218                 return NT_STATUS_NO_MEMORY; 
219         }
220
221         tmp_sname = talloc_strdup(mem_ctx, sname);
222         
223         /* Escape sname */
224         mysql_real_escape_string(data->handle, esc_sname, tmp_sname,
225                                                          strlen(tmp_sname));
226
227         talloc_free(tmp_sname);
228
229         if (user == NULL) {
230                 DEBUG(0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n"));
231                 talloc_free(mem_ctx);
232                 return NT_STATUS_INVALID_PARAMETER;
233         }
234
235         query = sql_account_query_select(mem_ctx, data->location, True, field, esc_sname);
236
237         talloc_free(esc_sname);
238
239         DEBUG(5, ("Executing query %s\n", query));
240         
241         mysql_ret = mysql_query(data->handle, query);
242         
243         talloc_free(query);
244         
245         if (mysql_ret) {
246                 DEBUG(0,
247                         ("Error while executing MySQL query %s\n", 
248                                 mysql_error(data->handle)));
249                 talloc_free(mem_ctx);
250                 return NT_STATUS_UNSUCCESSFUL;
251         }
252         
253         res = mysql_store_result(data->handle);
254         if (res == NULL) {
255                 DEBUG(0,
256                         ("Error storing results: %s\n", mysql_error(data->handle)));
257                 talloc_free(mem_ctx);
258                 return NT_STATUS_UNSUCCESSFUL;
259         }
260         
261         ret = row_to_sam_account(res, user);
262         mysql_free_result(res);
263         talloc_free(mem_ctx);
264
265         return ret;
266 }
267
268 /******************************************************************
269   Lookup a name in the SAM database
270  ******************************************************************/
271
272 static NTSTATUS mysqlsam_getsampwnam(struct pdb_methods *methods, SAM_ACCOUNT * user,
273                                          const char *sname)
274 {
275         struct pdb_mysql_data *data;
276
277         SET_DATA(data, methods);
278
279         if (!sname) {
280                 DEBUG(0, ("invalid name specified"));
281                 return NT_STATUS_INVALID_PARAMETER;
282         }
283
284         return mysqlsam_select_by_field(methods, user,
285                         SQL_SEARCH_USER_NAME, sname);
286 }
287
288
289 /***************************************************************************
290   Search by sid
291  **************************************************************************/
292
293 static NTSTATUS mysqlsam_getsampwsid(struct pdb_methods *methods, SAM_ACCOUNT * user,
294                                          const DOM_SID * sid)
295 {
296         struct pdb_mysql_data *data;
297         fstring sid_str;
298
299         SET_DATA(data, methods);
300
301         sid_to_string(sid_str, sid);
302
303         return mysqlsam_select_by_field(methods, user, SQL_SEARCH_USER_SID, sid_str);
304 }
305
306 /***************************************************************************
307   Delete a SAM_ACCOUNT
308  ****************************************************************************/
309
310 static NTSTATUS mysqlsam_delete_sam_account(struct pdb_methods *methods,
311                                                         SAM_ACCOUNT * sam_pass)
312 {
313         const char *sname = pdb_get_username(sam_pass);
314         char *esc;
315         char *query;
316         int ret;
317         struct pdb_mysql_data *data;
318         char *tmp_sname;
319         TALLOC_CTX *mem_ctx;
320         SET_DATA(data, methods);
321
322         if (!methods) {
323                 DEBUG(0, ("invalid methods!\n"));
324                 return NT_STATUS_INVALID_PARAMETER;
325         }
326
327         data = (struct pdb_mysql_data *) methods->private_data;
328         if (!data || !(data->handle)) {
329                 DEBUG(0, ("invalid handle!\n"));
330                 return NT_STATUS_INVALID_HANDLE;
331         }
332
333         if (!sname) {
334                 DEBUG(0, ("invalid name specified\n"));
335                 return NT_STATUS_INVALID_PARAMETER;
336         }
337
338         mem_ctx = talloc_init("mysqlsam_delete_sam_account");
339         
340         /* Escape sname */
341         esc = talloc_array(mem_ctx, char, strlen(sname) * 2 + 1);
342         if (!esc) {
343                 DEBUG(0, ("Can't allocate memory to store escaped name\n"));
344                 return NT_STATUS_NO_MEMORY;
345         }
346         
347         tmp_sname = talloc_strdup(mem_ctx, sname);
348         
349         mysql_real_escape_string(data->handle, esc, tmp_sname,
350                                                          strlen(tmp_sname));
351
352         talloc_free(tmp_sname);
353
354         query = sql_account_query_delete(mem_ctx, data->location, esc);
355
356         talloc_free(esc);
357
358         ret = mysql_query(data->handle, query);
359
360         talloc_free(query);
361
362         if (ret) {
363                 DEBUG(0,
364                           ("Error while executing query: %s\n",
365                            mysql_error(data->handle)));
366                 talloc_free(mem_ctx);
367                 return NT_STATUS_UNSUCCESSFUL;
368         }
369
370         DEBUG(5, ("User '%s' deleted\n", sname));
371         talloc_free(mem_ctx);
372         return NT_STATUS_OK;
373 }
374
375 static NTSTATUS mysqlsam_replace_sam_account(struct pdb_methods *methods,
376                                                          const SAM_ACCOUNT * newpwd, char isupdate)
377 {
378         struct pdb_mysql_data *data;
379         char *query;
380
381         if (!methods) {
382                 DEBUG(0, ("invalid methods!\n"));
383                 return NT_STATUS_INVALID_PARAMETER;
384         }
385
386         data = (struct pdb_mysql_data *) methods->private_data;
387
388         if (data == NULL || data->handle == NULL) {
389                 DEBUG(0, ("invalid handle!\n"));
390                 return NT_STATUS_INVALID_HANDLE;
391         }
392
393         query = sql_account_query_update(NULL, data->location, newpwd, isupdate);
394         if ( query == NULL ) /* Nothing to update. */
395                 return NT_STATUS_OK;
396         
397         /* Execute the query */
398         if (mysql_query(data->handle, query)) {
399                 DEBUG(0,
400                           ("Error executing %s, %s\n", query,
401                            mysql_error(data->handle)));
402                 talloc_free(query);
403                 return NT_STATUS_INVALID_PARAMETER;
404         }
405
406         talloc_free(query);
407
408         return NT_STATUS_OK;
409 }
410
411 static NTSTATUS mysqlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * newpwd)
412 {
413         return mysqlsam_replace_sam_account(methods, newpwd, 0);
414 }
415
416 static NTSTATUS mysqlsam_update_sam_account(struct pdb_methods *methods,
417                                                         SAM_ACCOUNT * newpwd)
418 {
419         return mysqlsam_replace_sam_account(methods, newpwd, 1);
420 }
421
422 static NTSTATUS mysqlsam_init(struct pdb_context * pdb_context, struct pdb_methods ** pdb_method,
423                  const char *location)
424 {
425         NTSTATUS nt_status;
426         struct pdb_mysql_data *data;
427
428         mysqlsam_debug_level = debug_add_class("mysqlsam");
429         if (mysqlsam_debug_level == -1) {
430                 mysqlsam_debug_level = DBGC_ALL;
431                 DEBUG(0,
432                           ("mysqlsam: Couldn't register custom debugging class!\n"));
433         }
434
435
436         if (!pdb_context) {
437                 DEBUG(0, ("invalid pdb_methods specified\n"));
438                 return NT_STATUS_UNSUCCESSFUL;
439         }
440
441         if (!NT_STATUS_IS_OK
442                 (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
443                 return nt_status;
444         }
445
446         (*pdb_method)->name = "mysqlsam";
447
448         (*pdb_method)->setsampwent = mysqlsam_setsampwent;
449         (*pdb_method)->endsampwent = mysqlsam_endsampwent;
450         (*pdb_method)->getsampwent = mysqlsam_getsampwent;
451         (*pdb_method)->getsampwnam = mysqlsam_getsampwnam;
452         (*pdb_method)->getsampwsid = mysqlsam_getsampwsid;
453         (*pdb_method)->add_sam_account = mysqlsam_add_sam_account;
454         (*pdb_method)->update_sam_account = mysqlsam_update_sam_account;
455         (*pdb_method)->delete_sam_account = mysqlsam_delete_sam_account;
456
457         data = talloc(pdb_context->mem_ctx, struct pdb_mysql_data);
458         (*pdb_method)->private_data = data;
459         data->handle = NULL;
460         data->pwent = NULL;
461
462         if (!location) {
463                 DEBUG(0, ("No identifier specified. Check the Samba HOWTO Collection for details\n"));
464                 return NT_STATUS_INVALID_PARAMETER;
465         }
466
467         data->location = smb_xstrdup(location);
468
469         DEBUG(1,
470                   ("Connecting to database server, host: %s, user: %s, database: %s, port: %ld\n",
471                    config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
472                    config_value(data, "mysql user", CONFIG_USER_DEFAULT),
473                    config_value(data, "mysql database", CONFIG_DB_DEFAULT),
474                    xatol(config_value(data, "mysql port", CONFIG_PORT_DEFAULT))));
475
476         /* Do the mysql initialization */
477         data->handle = mysql_init(NULL);
478         if (!data->handle) {
479                 DEBUG(0, ("Failed to connect to server\n"));
480                 return NT_STATUS_UNSUCCESSFUL;
481         }
482
483         if(!sql_account_config_valid(data->location)) {
484                 return NT_STATUS_INVALID_PARAMETER;
485         }
486         
487         /* Process correct entry in $HOME/.my.conf */
488         if (!mysql_real_connect(data->handle,
489                         config_value(data, "mysql host", CONFIG_HOST_DEFAULT),
490                         config_value(data, "mysql user", CONFIG_USER_DEFAULT),
491                         config_value(data, "mysql password", CONFIG_PASS_DEFAULT),
492                         config_value(data, "mysql database", CONFIG_DB_DEFAULT),
493                         xatol(config_value (data, "mysql port", CONFIG_PORT_DEFAULT)), 
494                         NULL, 0)) {
495                 DEBUG(0,
496                           ("Failed to connect to mysql database: error: %s\n",
497                            mysql_error(data->handle)));
498                 return NT_STATUS_UNSUCCESSFUL;
499         }
500         
501         DEBUG(5, ("Connected to mysql db\n"));
502
503         return NT_STATUS_OK;
504 }
505
506 NTSTATUS pdb_mysql_init(void) 
507 {
508         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "mysql", mysqlsam_init);
509 }