2 * PostgresSQL password backend for samba
3 * Copyright (C) Hamish Friedlander 2003
4 * Copyright (C) Jelmer Vernooij 2004
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)
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
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.
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"
30 /* handles for doing db transactions */
31 typedef struct pdb_pgsql_data
{
32 PGconn
*master_handle
;
43 const char *location
;
46 struct pdb_context
*the_pdb_context
;
48 #define SET_DATA(data,methods) { \
50 DEBUG(0, ("invalid methods!\n")); \
51 return NT_STATUS_INVALID_PARAMETER; \
53 data = (struct pdb_pgsql_data *)methods->private_data; \
57 #define SET_DATA_QUIET(data,methods) { \
59 DEBUG(0, ("invalid methods!\n")); \
62 data = (struct pdb_pgsql_data *)methods->private_data; \
66 #define config_value( data, name, default_value ) \
67 lp_parm_const_string( GLOBAL_SECTION_SNUM, (data)->location, name, default_value )
69 static PGconn
*pgsqlsam_connect( struct pdb_pgsql_data
*data
)
77 "Connecting to database server, host: %s, user: %s, password: XXXXXX, database: %s, port: %s\n",
78 data
->host
, data
->user
, data
->db
, data
->port
82 /* Do the pgsql initialization */
83 handle
= PQsetdbLogin(
93 if ( handle
!= NULL
&& PQstatus( handle
) != CONNECTION_OK
)
95 DEBUG( 0, ("Failed to connect to pgsql database: error: %s\n",
96 (handle
!= NULL
? PQerrorMessage( handle
) : "")) ) ;
100 DEBUG( 5, ("Connected to pgsql database\n") ) ;
104 /* The assumption here is that the master process will get connection 0,
105 * and all the renaining ones just one connection for their etire life span.
107 static PGconn
*choose_connection( struct pdb_pgsql_data
*data
)
109 if ( data
->master_handle
== NULL
)
111 data
->master_handle
= pgsqlsam_connect( data
);
112 return data
->master_handle
;
115 /* Master connection != NULL, so we are just another process. */
117 /* If we didn't connect yet, do it now. */
118 if ( data
->handle
== NULL
)
120 data
->handle
= pgsqlsam_connect( data
);
123 return data
->handle
;
126 static long PQgetlong( PGresult
*r
, long row
, long col
)
128 if ( PQgetisnull( r
, row
, col
) ) return 0 ;
130 return atol( PQgetvalue( r
, row
, col
) ) ;
133 static NTSTATUS
row_to_sam_account ( PGresult
*r
, long row
, SAM_ACCOUNT
*u
)
137 unsigned char *hours
;
138 size_t hours_len
= 0 ;
140 if ( row
>= PQntuples( r
) ) return NT_STATUS_INVALID_PARAMETER
;
142 pdb_set_logon_time ( u
, PQgetlong ( r
, row
, 0 ), PDB_SET
) ;
143 pdb_set_logoff_time ( u
, PQgetlong ( r
, row
, 1 ), PDB_SET
) ;
144 pdb_set_kickoff_time ( u
, PQgetlong ( r
, row
, 2 ), PDB_SET
) ;
145 pdb_set_pass_last_set_time ( u
, PQgetlong ( r
, row
, 3 ), PDB_SET
) ;
146 pdb_set_pass_can_change_time ( u
, PQgetlong ( r
, row
, 4 ), PDB_SET
) ;
147 pdb_set_pass_must_change_time( u
, PQgetlong ( r
, row
, 5 ), PDB_SET
) ;
148 pdb_set_username ( u
, PQgetvalue( r
, row
, 6 ), PDB_SET
) ;
149 pdb_set_domain ( u
, PQgetvalue( r
, row
, 7 ), PDB_SET
) ;
150 pdb_set_nt_username ( u
, PQgetvalue( r
, row
, 8 ), PDB_SET
) ;
151 pdb_set_fullname ( u
, PQgetvalue( r
, row
, 9 ), PDB_SET
) ;
152 pdb_set_homedir ( u
, PQgetvalue( r
, row
, 10 ), PDB_SET
) ;
153 pdb_set_dir_drive ( u
, PQgetvalue( r
, row
, 11 ), PDB_SET
) ;
154 pdb_set_logon_script ( u
, PQgetvalue( r
, row
, 12 ), PDB_SET
) ;
155 pdb_set_profile_path ( u
, PQgetvalue( r
, row
, 13 ), PDB_SET
) ;
156 pdb_set_acct_desc ( u
, PQgetvalue( r
, row
, 14 ), PDB_SET
) ;
157 pdb_set_workstations ( u
, PQgetvalue( r
, row
, 15 ), PDB_SET
) ;
158 pdb_set_unknown_str ( u
, PQgetvalue( r
, row
, 16 ), PDB_SET
) ;
159 pdb_set_munged_dial ( u
, PQgetvalue( r
, row
, 17 ), PDB_SET
) ;
161 pdb_set_acct_ctrl ( u
, PQgetlong ( r
, row
, 23 ), PDB_SET
) ;
162 pdb_set_logon_divs ( u
, PQgetlong ( r
, row
, 24 ), PDB_SET
) ;
163 pdb_set_hours_len ( u
, PQgetlong ( r
, row
, 25 ), PDB_SET
) ;
164 pdb_set_bad_password_count ( u
, PQgetlong ( r
, row
, 26 ), PDB_SET
) ;
165 pdb_set_logon_count ( u
, PQgetlong ( r
, row
, 27 ), PDB_SET
) ;
166 pdb_set_unknown_6 ( u
, PQgetlong ( r
, row
, 28 ), PDB_SET
) ;
167 hours
= PQgetvalue ( r
, row
, 29 );
168 if ( hours
!= NULL
) {
169 hours
= PQunescapeBytea ( hours
, &hours_len
) ;
171 pdb_set_hours ( u
, hours
, PDB_SET
) ;
175 if ( !PQgetisnull( r
, row
, 18 ) ) {
176 string_to_sid( &sid
, PQgetvalue( r
, row
, 18 ) ) ;
177 pdb_set_user_sid ( u
, &sid
, PDB_SET
) ;
180 if ( !PQgetisnull( r
, row
, 19 ) ) {
181 string_to_sid( &sid
, PQgetvalue( r
, row
, 19 ) ) ;
182 pdb_set_group_sid( u
, &sid
, PDB_SET
) ;
185 if ( pdb_gethexpwd( PQgetvalue( r
, row
, 20 ), temp
), PDB_SET
) pdb_set_lanman_passwd( u
, temp
, PDB_SET
) ;
186 if ( pdb_gethexpwd( PQgetvalue( r
, row
, 21 ), temp
), PDB_SET
) pdb_set_nt_passwd ( u
, temp
, PDB_SET
) ;
188 /* Only use plaintext password storage when lanman and nt are NOT used */
189 if ( PQgetisnull( r
, row
, 20 ) || PQgetisnull( r
, row
, 21 ) ) pdb_set_plaintext_passwd( u
, PQgetvalue( r
, row
, 22 ) ) ;
191 return NT_STATUS_OK
;
194 static NTSTATUS
pgsqlsam_setsampwent(struct pdb_methods
*methods
, BOOL update
, uint16 acb_mask
)
196 struct pdb_pgsql_data
*data
;
201 SET_DATA( data
, methods
) ;
203 /* Connect to the DB. */
204 handle
= choose_connection( data
);
205 if ( handle
== NULL
)
206 return NT_STATUS_UNSUCCESSFUL
;
207 DEBUG( 5, ("CONNECTING pgsqlsam_setsampwent\n") ) ;
209 query
= sql_account_query_select(NULL
, data
->location
, update
, SQL_SEARCH_NONE
, NULL
);
212 DEBUG( 5, ("Executing query %s\n", query
) ) ;
213 data
->pwent
= PQexec( handle
, query
) ;
217 if ( data
->pwent
== NULL
)
219 DEBUG( 0, ("Error executing %s, %s\n", query
, PQerrorMessage( handle
) ) ) ;
220 retval
= NT_STATUS_UNSUCCESSFUL
;
222 else if ( PQresultStatus( data
->pwent
) != PGRES_TUPLES_OK
)
224 DEBUG( 0, ("Error executing %s, %s\n", query
, PQresultErrorMessage( data
->pwent
) ) ) ;
225 retval
= NT_STATUS_UNSUCCESSFUL
;
229 DEBUG( 5, ("pgsqlsam_setsampwent succeeded(%d results)!\n", PQntuples(data
->pwent
)) ) ;
230 retval
= NT_STATUS_OK
;
237 /***************************************************************
238 End enumeration of the passwd list.
239 ****************************************************************/
241 static void pgsqlsam_endsampwent(struct pdb_methods
*methods
)
243 struct pdb_pgsql_data
*data
;
245 SET_DATA_QUIET( data
, methods
) ;
247 if (data
->pwent
!= NULL
)
249 PQclear( data
->pwent
) ;
255 DEBUG( 5, ("pgsql_endsampwent called\n") ) ;
258 /*****************************************************************
259 Get one SAM_ACCOUNT from the list (next in line)
260 *****************************************************************/
262 static NTSTATUS
pgsqlsam_getsampwent( struct pdb_methods
*methods
, SAM_ACCOUNT
*user
)
264 struct pdb_pgsql_data
*data
;
267 SET_DATA( data
, methods
) ;
269 if ( data
->pwent
== NULL
)
271 DEBUG( 0, ("invalid pwent\n") ) ;
272 return NT_STATUS_INVALID_PARAMETER
;
275 retval
= row_to_sam_account( data
->pwent
, data
->currow
, user
) ;
281 static NTSTATUS
pgsqlsam_select_by_field ( struct pdb_methods
*methods
, SAM_ACCOUNT
*user
, enum sql_search_field field
, const char *sname
)
283 struct pdb_pgsql_data
*data
;
292 SET_DATA(data
, methods
);
296 DEBUG( 0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n") ) ;
297 return NT_STATUS_INVALID_PARAMETER
;
300 DEBUG( 5, ("pgsqlsam_select_by_field: getting data where %d = %s(nonescaped)\n", field
, sname
) ) ;
303 esc
= talloc_array(NULL
, char, strlen(sname
) * 2 + 1);
306 DEBUG(0, ("Can't allocate memory to store escaped name\n"));
307 return NT_STATUS_NO_MEMORY
;
310 //tmp_sname = smb_xstrdup(sname);
311 PQescapeString( esc
, sname
, strlen(sname
) ) ;
313 /* Connect to the DB. */
314 handle
= choose_connection( data
);
315 if ( handle
== NULL
)
316 return NT_STATUS_UNSUCCESSFUL
;
318 query
= sql_account_query_select(NULL
, data
->location
, True
, field
, esc
);
321 DEBUG( 5, ("Executing query %s\n", query
) ) ;
322 result
= PQexec( handle
, query
) ;
325 if ( result
== NULL
)
327 DEBUG( 0, ("Error executing %s, %s\n", query
, PQerrorMessage( handle
) ) ) ;
328 retval
= NT_STATUS_UNSUCCESSFUL
;
330 else if ( PQresultStatus( result
) != PGRES_TUPLES_OK
)
332 DEBUG( 0, ("Error executing %s, %s\n", query
, PQresultErrorMessage( result
) ) ) ;
333 retval
= NT_STATUS_UNSUCCESSFUL
;
337 retval
= row_to_sam_account( result
, 0, user
) ;
341 talloc_free( query
) ;
343 if ( result
!= NULL
)
349 /******************************************************************
350 Lookup a name in the SAM database
351 ******************************************************************/
353 static NTSTATUS
pgsqlsam_getsampwnam ( struct pdb_methods
*methods
, SAM_ACCOUNT
*user
, const char *sname
)
355 struct pdb_pgsql_data
*data
;
360 SET_DATA(data
, methods
);
364 DEBUG( 0, ("invalid name specified") ) ;
365 return NT_STATUS_INVALID_PARAMETER
;
368 lowercasename
= smb_xstrdup(sname
);
369 l
= strlen(lowercasename
);
370 for(i
= 0; i
< l
; i
++) {
371 lowercasename
[i
] = tolower(lowercasename
[i
]);
374 result
= pgsqlsam_select_by_field( methods
, user
, SQL_SEARCH_USER_NAME
, lowercasename
) ;
376 SAFE_FREE( lowercasename
) ;
382 /***************************************************************************
384 **************************************************************************/
386 static NTSTATUS
pgsqlsam_getsampwsid ( struct pdb_methods
*methods
, SAM_ACCOUNT
*user
, const DOM_SID
*sid
)
388 struct pdb_pgsql_data
*data
;
391 SET_DATA( data
, methods
) ;
393 sid_to_string( sid_str
, sid
) ;
395 return pgsqlsam_select_by_field( methods
, user
, SQL_SEARCH_USER_SID
, sid_str
) ;
398 /***************************************************************************
400 ****************************************************************************/
402 static NTSTATUS
pgsqlsam_delete_sam_account( struct pdb_methods
*methods
, SAM_ACCOUNT
*sam_pass
)
404 struct pdb_pgsql_data
*data
;
407 const char *sname
= pdb_get_username( sam_pass
) ;
414 SET_DATA(data
, methods
);
418 DEBUG( 0, ("invalid name specified\n") ) ;
419 return NT_STATUS_INVALID_PARAMETER
;
423 esc
= talloc_array(NULL
, char, strlen(sname
) * 2 + 1);
426 DEBUG(0, ("Can't allocate memory to store escaped name\n"));
427 return NT_STATUS_NO_MEMORY
;
430 PQescapeString( esc
, sname
, strlen(sname
) ) ;
432 /* Connect to the DB. */
433 handle
= choose_connection( data
);
434 if ( handle
== NULL
)
435 return NT_STATUS_UNSUCCESSFUL
;
437 query
= sql_account_query_delete(NULL
, data
->location
, esc
);
440 result
= PQexec( handle
, query
) ;
442 if ( result
== NULL
)
444 DEBUG( 0, ("Error executing %s, %s\n", query
, PQerrorMessage( handle
) ) ) ;
445 retval
= NT_STATUS_UNSUCCESSFUL
;
447 else if ( PQresultStatus( result
) != PGRES_COMMAND_OK
)
449 DEBUG( 0, ("Error executing %s, %s\n", query
, PQresultErrorMessage( result
) ) ) ;
450 retval
= NT_STATUS_UNSUCCESSFUL
;
454 DEBUG( 5, ("User '%s' deleted\n", sname
) ) ;
455 retval
= NT_STATUS_OK
;
458 if ( result
!= NULL
)
461 talloc_free( query
) ;
466 static NTSTATUS
pgsqlsam_replace_sam_account( struct pdb_methods
*methods
, const SAM_ACCOUNT
*newpwd
, char isupdate
)
468 struct pdb_pgsql_data
*data
;
476 DEBUG( 0, ("invalid methods!\n") ) ;
477 return NT_STATUS_INVALID_PARAMETER
;
480 data
= (struct pdb_pgsql_data
*) methods
->private_data
;
482 if ( data
== NULL
|| handle
== NULL
)
484 DEBUG( 0, ("invalid handle!\n") ) ;
485 return NT_STATUS_INVALID_HANDLE
;
488 query
= sql_account_query_update(NULL
, data
->location
, newpwd
, isupdate
);
489 if ( query
== NULL
) /* Nothing to update. */
492 /* Connect to the DB. */
493 handle
= choose_connection( data
);
494 if ( handle
== NULL
)
495 return NT_STATUS_UNSUCCESSFUL
;
497 result
= PQexec( handle
, query
) ;
499 /* Execute the query */
500 if ( result
== NULL
)
502 DEBUG( 0, ("Error executing %s, %s\n", query
, PQerrorMessage( handle
) ) ) ;
503 retval
= NT_STATUS_INVALID_PARAMETER
;
505 else if ( PQresultStatus( result
) != PGRES_COMMAND_OK
)
507 DEBUG( 0, ("Error executing %s, %s\n", query
, PQresultErrorMessage( result
) ) ) ;
508 retval
= NT_STATUS_INVALID_PARAMETER
;
512 retval
= NT_STATUS_OK
;
514 if ( result
!= NULL
)
521 static NTSTATUS
pgsqlsam_add_sam_account ( struct pdb_methods
*methods
, SAM_ACCOUNT
*newpwd
)
523 return pgsqlsam_replace_sam_account( methods
, newpwd
, 0 ) ;
526 static NTSTATUS
pgsqlsam_update_sam_account ( struct pdb_methods
*methods
, SAM_ACCOUNT
*newpwd
)
528 return pgsqlsam_replace_sam_account( methods
, newpwd
, 1 ) ;
531 static NTSTATUS
pgsqlsam_init ( struct pdb_context
*pdb_context
, struct pdb_methods
**pdb_method
, const char *location
)
534 struct pdb_pgsql_data
*data
;
538 DEBUG( 0, ("invalid pdb_methods specified\n") ) ;
539 return NT_STATUS_UNSUCCESSFUL
;
542 the_pdb_context
= pdb_context
;
545 (nt_status
= make_pdb_methods(pdb_context
->mem_ctx
, pdb_method
))) {
549 (*pdb_method
)->name
= "pgsqlsam" ;
551 (*pdb_method
)->setsampwent
= pgsqlsam_setsampwent
;
552 (*pdb_method
)->endsampwent
= pgsqlsam_endsampwent
;
553 (*pdb_method
)->getsampwent
= pgsqlsam_getsampwent
;
554 (*pdb_method
)->getsampwnam
= pgsqlsam_getsampwnam
;
555 (*pdb_method
)->getsampwsid
= pgsqlsam_getsampwsid
;
556 (*pdb_method
)->add_sam_account
= pgsqlsam_add_sam_account
;
557 (*pdb_method
)->update_sam_account
= pgsqlsam_update_sam_account
;
558 (*pdb_method
)->delete_sam_account
= pgsqlsam_delete_sam_account
;
560 data
= talloc( pdb_context
->mem_ctx
, struct pdb_pgsql_data
) ;
561 (*pdb_method
)->private_data
= data
;
563 data
->master_handle
= NULL
;
569 DEBUG( 0, ("No identifier specified. Check the Samba HOWTO Collection for details\n") ) ;
570 return NT_STATUS_INVALID_PARAMETER
;
573 data
->location
= smb_xstrdup( location
) ;
575 if(!sql_account_config_valid(data
->location
)) {
576 return NT_STATUS_INVALID_PARAMETER
;
583 "Database server parameters: host: %s, user: %s, password: XXXX, database: %s, port: %s\n",
584 config_value( data
, "pgsql host" , CONFIG_HOST_DEFAULT
),
585 config_value( data
, "pgsql user" , CONFIG_USER_DEFAULT
),
586 config_value( data
, "pgsql database", CONFIG_DB_DEFAULT
),
587 config_value( data
, "pgsql port" , CONFIG_PORT_DEFAULT
)
591 /* Save the parameters. */
592 data
->db
= config_value( data
, "pgsql database", CONFIG_DB_DEFAULT
);
593 data
->host
= config_value( data
, "pgsql host" , CONFIG_HOST_DEFAULT
);
594 data
->port
= config_value( data
, "pgsql port" , CONFIG_PORT_DEFAULT
);
595 data
->user
= config_value( data
, "pgsql user" , CONFIG_USER_DEFAULT
);
596 data
->pass
= config_value( data
, "pgsql password", CONFIG_PASS_DEFAULT
);
598 DEBUG( 5, ("Pgsql module intialized\n") ) ;
602 NTSTATUS
pdb_pgsql_init(void)
604 return smb_register_passdb( PASSDB_INTERFACE_VERSION
, "pgsql", pgsqlsam_init
) ;