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 29static int mysqlsam_debug_level = DBGC_ALL; 30 31#undef DBGC_CLASS 32#define DBGC_CLASS mysqlsam_debug_level 33 34typedef 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 55static long xatol(const char *d) 56{ 57 if(!d) return 0; 58 return atol(d); 59} 60 61static 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 123static 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(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 165static 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 187static 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 201static 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 267static 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 288static 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 305static 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 366static 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 398static NTSTATUS mysqlsam_add_sam_account(struct pdb_methods *methods, SAM_ACCOUNT * newpwd) 399{ 400 return mysqlsam_replace_sam_account(methods, newpwd, 0); 401} 402 403static 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 409static 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, 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 database", CONFIG_DB_DEFAULT), 461 xatol(config_value(data, "mysql port", CONFIG_PORT_DEFAULT)))); 462 463 /* Do the mysql initialization */ 464 data->handle = mysql_init(NULL); 465 if (!data->handle) { 466 DEBUG(0, ("Failed to connect to server\n")); 467 return NT_STATUS_UNSUCCESSFUL; 468 } 469 470 if(!sql_account_config_valid(data->location)) { 471 return NT_STATUS_INVALID_PARAMETER; 472 } 473 474 /* Process correct entry in $HOME/.my.conf */ 475 if (!mysql_real_connect(data->handle, 476 config_value(data, "mysql host", CONFIG_HOST_DEFAULT), 477 config_value(data, "mysql user", CONFIG_USER_DEFAULT), 478 config_value(data, "mysql password", CONFIG_PASS_DEFAULT), 479 config_value(data, "mysql database", CONFIG_DB_DEFAULT), 480 xatol(config_value (data, "mysql port", CONFIG_PORT_DEFAULT)), 481 NULL, 0)) { 482 DEBUG(0, 483 ("Failed to connect to mysql database: error: %s\n", 484 mysql_error(data->handle))); 485 return NT_STATUS_UNSUCCESSFUL; 486 } 487 488 DEBUG(5, ("Connected to mysql db\n")); 489 490 return NT_STATUS_OK; 491} 492 493NTSTATUS pdb_mysql_init(void) 494{ 495 return smb_register_passdb(PASSDB_INTERFACE_VERSION, "mysql", mysqlsam_init); 496} 497