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