version incremented
[samba] / source / passdb / pdb_pgsql.c
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 */
31 typedef struct pdb_pgsql_data {
32   PGconn     *master_handle ;
33   PGconn     *handle ;
34
35   PGresult   *pwent  ;
36   long        currow ;
37   const char *db     ;
38   const char *host   ;
39   const char *port   ;
40   const char *user   ;
41   const char *pass   ;
42
43   const char *location ;
44 } pdb_pgsql_data ;
45
46 struct pdb_context *the_pdb_context;
47
48 #define SET_DATA(data,methods) { \
49         if(!methods){ \
50                 DEBUG(0, ("invalid methods!\n")); \
51                         return NT_STATUS_INVALID_PARAMETER; \
52         } \
53         data = (struct pdb_pgsql_data *)methods->private_data; \
54 }
55
56
57 #define SET_DATA_QUIET(data,methods) { \
58         if(!methods){ \
59                 DEBUG(0, ("invalid methods!\n")); \
60                         return ; \
61         } \
62         data = (struct pdb_pgsql_data *)methods->private_data; \
63 }
64
65
66 #define config_value( data, name, default_value ) \
67   lp_parm_const_string( GLOBAL_SECTION_SNUM, (data)->location, name, default_value )
68
69 static PGconn *pgsqlsam_connect( struct pdb_pgsql_data *data )
70 {
71   PGconn *handle;
72   
73   DEBUG
74   (
75     1, 
76     (
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
79     )
80   ) ;
81   
82   /* Do the pgsql initialization */
83   handle = PQsetdbLogin( 
84                          data->host,
85                          data->port,
86                          NULL,
87                          NULL,
88                          data->db,
89                          data->user,
90                          data->pass
91                        ) ;
92   
93   if ( handle != NULL && PQstatus( handle ) != CONNECTION_OK )
94   {
95     DEBUG( 0, ("Failed to connect to pgsql database: error: %s\n",
96                 (handle != NULL ? PQerrorMessage( handle ) : "")) ) ;
97     return NULL;
98   }
99   
100   DEBUG( 5, ("Connected to pgsql database\n") ) ;
101   return handle;
102 }
103
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.
106  */
107 static PGconn *choose_connection( struct pdb_pgsql_data *data )
108 {
109   if ( data->master_handle == NULL )
110   {
111     data->master_handle = pgsqlsam_connect( data );
112     return data->master_handle ;
113   }
114
115   /* Master connection != NULL, so we are just another process. */
116
117   /* If we didn't connect yet, do it now. */
118   if ( data->handle == NULL )
119   {
120     data->handle = pgsqlsam_connect( data );
121   }
122
123   return data->handle ;
124 }
125
126 static long PQgetlong( PGresult *r, long row, long col )
127 {
128   if ( PQgetisnull( r, row, col ) ) return 0 ;
129   
130   return atol( PQgetvalue( r, row,  col ) ) ;
131 }
132
133 static NTSTATUS row_to_sam_account ( PGresult *r, long row, SAM_ACCOUNT *u )
134 {
135   pstring temp ;
136   DOM_SID sid ;
137   unsigned char *hours ;
138   size_t hours_len = 0 ;
139   
140   if ( row >= PQntuples( r ) ) return NT_STATUS_INVALID_PARAMETER ;
141
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 ) ;
160   
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 ) ;
170     if ( hours_len > 0 )
171        pdb_set_hours            ( u, hours, PDB_SET ) ;
172     free ( hours );
173   }
174   
175   if ( !PQgetisnull( r, row, 18 ) ) {
176     string_to_sid( &sid, PQgetvalue( r, row, 18 ) ) ;
177     pdb_set_user_sid ( u, &sid, PDB_SET ) ;
178   }
179
180   if ( !PQgetisnull( r, row, 19 ) ) {
181     string_to_sid( &sid, PQgetvalue( r, row, 19 ) ) ;
182     pdb_set_group_sid( u, &sid, PDB_SET ) ;
183   }
184   
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 ) ;
187   
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 ) ) ;
190   
191   return NT_STATUS_OK ;
192 }
193
194 static NTSTATUS pgsqlsam_setsampwent(struct pdb_methods *methods, BOOL update, uint16 acb_mask)
195 {
196   struct pdb_pgsql_data *data ;
197   PGconn *handle ;
198   char *query ;
199   NTSTATUS retval ;
200   
201   SET_DATA( data, methods ) ;
202
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") ) ;
208   
209   query = sql_account_query_select(NULL, data->location, update, SQL_SEARCH_NONE, NULL);
210   
211   /* Do it */
212   DEBUG( 5, ("Executing query %s\n", query) ) ;
213   data->pwent  = PQexec( handle, query ) ;
214   data->currow = 0 ;
215   
216   /* Result? */
217   if ( data->pwent == NULL )
218   {
219     DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( handle ) ) ) ;
220     retval = NT_STATUS_UNSUCCESSFUL ;
221   }
222   else if ( PQresultStatus( data->pwent ) != PGRES_TUPLES_OK )
223   {
224     DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( data->pwent ) ) ) ;
225     retval = NT_STATUS_UNSUCCESSFUL ;
226   }
227   else
228   {
229     DEBUG( 5, ("pgsqlsam_setsampwent succeeded(%d results)!\n", PQntuples(data->pwent)) ) ;
230     retval = NT_STATUS_OK ;
231   }
232
233   talloc_free(query);
234   return retval ;
235 }
236
237 /***************************************************************
238   End enumeration of the passwd list.
239  ****************************************************************/
240
241 static void pgsqlsam_endsampwent(struct pdb_methods *methods)
242 {
243   struct pdb_pgsql_data *data ; 
244     
245   SET_DATA_QUIET( data, methods ) ;
246   
247   if (data->pwent != NULL)
248   {
249     PQclear( data->pwent ) ;
250   }
251   
252   data->pwent  = NULL ;
253   data->currow = 0 ;
254   
255   DEBUG( 5, ("pgsql_endsampwent called\n") ) ;
256 }
257
258 /*****************************************************************
259   Get one SAM_ACCOUNT from the list (next in line)
260  *****************************************************************/
261
262 static NTSTATUS pgsqlsam_getsampwent( struct pdb_methods *methods, SAM_ACCOUNT *user )
263 {
264   struct pdb_pgsql_data *data;
265   NTSTATUS retval ;
266   
267   SET_DATA( data, methods ) ;
268   
269   if ( data->pwent == NULL )
270   {
271     DEBUG( 0, ("invalid pwent\n") ) ;
272     return NT_STATUS_INVALID_PARAMETER ;
273   }
274   
275   retval = row_to_sam_account( data->pwent, data->currow, user ) ;
276   data->currow++ ;
277   
278   return retval ;
279 }
280
281 static NTSTATUS pgsqlsam_select_by_field ( struct pdb_methods *methods, SAM_ACCOUNT *user, enum sql_search_field field, const char *sname )
282 {
283   struct pdb_pgsql_data *data ;
284   PGconn *handle ;
285   
286   char *esc ;
287   char *query ;
288   
289   PGresult *result ;
290   NTSTATUS retval ;
291
292   SET_DATA(data, methods);
293
294   if ( user == NULL )
295   {
296     DEBUG( 0, ("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n") ) ;
297     return NT_STATUS_INVALID_PARAMETER;
298   }
299   
300   DEBUG( 5, ("pgsqlsam_select_by_field: getting data where %d = %s(nonescaped)\n", field, sname) ) ;
301   
302   /* Escape sname */
303   esc = talloc_array(NULL, char, strlen(sname) * 2 + 1);
304   if ( !esc )
305   {
306     DEBUG(0, ("Can't allocate memory to store escaped name\n"));
307     return NT_STATUS_NO_MEMORY; 
308   }
309   
310   //tmp_sname = smb_xstrdup(sname);
311   PQescapeString( esc, sname, strlen(sname) ) ;
312
313   /* Connect to the DB. */
314   handle = choose_connection( data );
315   if ( handle == NULL )
316     return NT_STATUS_UNSUCCESSFUL ;
317   
318   query = sql_account_query_select(NULL, data->location, True, field, esc);
319   
320   /* Do it */
321   DEBUG( 5, ("Executing query %s\n", query) ) ;
322   result = PQexec( handle, query ) ;
323   
324   /* Result? */
325   if ( result == NULL )
326   {
327     DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( handle ) ) ) ;
328     retval = NT_STATUS_UNSUCCESSFUL ;
329   }
330   else if ( PQresultStatus( result ) != PGRES_TUPLES_OK )
331   {
332     DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( result ) ) ) ;
333     retval = NT_STATUS_UNSUCCESSFUL ;
334   }
335   else
336   {
337     retval = row_to_sam_account( result, 0, user ) ;
338   }
339   
340   talloc_free( esc   ) ;
341   talloc_free( query ) ;
342  
343   if ( result != NULL )
344     PQclear( result ) ;
345   
346   return retval ;
347 }
348
349 /******************************************************************
350   Lookup a name in the SAM database
351  ******************************************************************/
352
353 static NTSTATUS pgsqlsam_getsampwnam ( struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname )
354 {
355   struct pdb_pgsql_data *data;
356   size_t i, l;
357   char *lowercasename;
358   NTSTATUS result;
359   
360   SET_DATA(data, methods);
361   
362   if ( !sname )
363   {
364     DEBUG( 0, ("invalid name specified") ) ;
365     return NT_STATUS_INVALID_PARAMETER;
366   }
367
368   lowercasename = smb_xstrdup(sname);
369   l = strlen(lowercasename);
370   for(i = 0; i < l; i++) {
371     lowercasename[i] = tolower_ascii(lowercasename[i]);
372   }
373   
374   result = pgsqlsam_select_by_field( methods, user, SQL_SEARCH_USER_NAME, lowercasename ) ;
375
376   SAFE_FREE( lowercasename ) ;
377
378   return result;
379 }
380
381
382 /***************************************************************************
383   Search by sid
384  **************************************************************************/
385
386 static NTSTATUS pgsqlsam_getsampwsid ( struct pdb_methods *methods, SAM_ACCOUNT *user, const DOM_SID *sid )
387 {
388   struct pdb_pgsql_data *data;
389   fstring sid_str;
390   
391   SET_DATA( data, methods ) ;
392   
393   sid_to_string( sid_str, sid ) ;
394   
395   return pgsqlsam_select_by_field( methods, user, SQL_SEARCH_USER_SID, sid_str ) ;
396 }
397
398 /***************************************************************************
399   Delete a SAM_ACCOUNT
400  ****************************************************************************/
401
402 static NTSTATUS pgsqlsam_delete_sam_account( struct pdb_methods *methods, SAM_ACCOUNT *sam_pass )
403 {
404   struct pdb_pgsql_data *data ;
405   PGconn *handle ;
406   
407   const char *sname = pdb_get_username( sam_pass ) ;
408   char *esc ;
409   char *query ;
410   
411   PGresult *result ;
412   NTSTATUS retval ;
413   
414   SET_DATA(data, methods);
415   
416   if ( !sname )
417   {
418     DEBUG( 0, ("invalid name specified\n") ) ;
419     return NT_STATUS_INVALID_PARAMETER ;
420   }
421   
422   /* Escape sname */
423   esc = talloc_array(NULL, char, strlen(sname) * 2 + 1);
424   if ( !esc )
425   {
426     DEBUG(0, ("Can't allocate memory to store escaped name\n"));
427     return NT_STATUS_NO_MEMORY;
428   }
429   
430   PQescapeString( esc, sname, strlen(sname) ) ;
431
432   /* Connect to the DB. */
433   handle = choose_connection( data );
434   if ( handle == NULL )
435     return NT_STATUS_UNSUCCESSFUL ;
436   
437   query = sql_account_query_delete(NULL, data->location, esc);
438   
439   /* Do it */
440   result = PQexec( handle, query ) ;
441   
442   if ( result == NULL )
443   {
444     DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( handle ) ) ) ;
445     retval = NT_STATUS_UNSUCCESSFUL ;
446   }
447   else if ( PQresultStatus( result ) != PGRES_COMMAND_OK )
448   {
449     DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( result ) ) ) ;
450     retval = NT_STATUS_UNSUCCESSFUL ;
451   }
452   else
453   {
454     DEBUG( 5, ("User '%s' deleted\n", sname) ) ;
455     retval = NT_STATUS_OK ;
456   }
457   
458   if ( result != NULL )
459     PQclear( result ) ;
460   talloc_free( esc ) ;
461   talloc_free( query ) ;
462   
463   return retval ;
464 }
465
466 static NTSTATUS pgsqlsam_replace_sam_account( struct pdb_methods *methods, const SAM_ACCOUNT *newpwd, char isupdate )
467 {
468   struct pdb_pgsql_data *data ;
469   PGconn *handle ;
470   char *query;
471   PGresult *result ;
472   NTSTATUS retval ;
473   
474   if ( !methods )
475   {
476     DEBUG( 0, ("invalid methods!\n") ) ;
477     return NT_STATUS_INVALID_PARAMETER ;
478   }
479   
480   data = (struct pdb_pgsql_data *) methods->private_data ;
481   
482   if ( data == NULL || handle == NULL )
483   {
484     DEBUG( 0, ("invalid handle!\n") ) ;
485     return NT_STATUS_INVALID_HANDLE ;
486   }
487
488   query = sql_account_query_update(NULL, data->location, newpwd, isupdate);
489   if ( query == NULL ) /* Nothing to update. */
490     return NT_STATUS_OK;
491
492   /* Connect to the DB. */
493   handle = choose_connection( data );
494   if ( handle == NULL )
495     return NT_STATUS_UNSUCCESSFUL ;
496
497   result = PQexec( handle, query ) ;
498   
499   /* Execute the query */
500   if ( result == NULL )
501   {
502     DEBUG( 0, ("Error executing %s, %s\n", query, PQerrorMessage( handle ) ) ) ;
503     retval = NT_STATUS_INVALID_PARAMETER;
504   }
505   else if ( PQresultStatus( result ) != PGRES_COMMAND_OK )
506   {
507     DEBUG( 0, ("Error executing %s, %s\n", query, PQresultErrorMessage( result ) ) ) ;
508     retval = NT_STATUS_INVALID_PARAMETER;
509   }
510   else
511   {
512     retval = NT_STATUS_OK;
513   }
514   if ( result != NULL )
515     PQclear( result ) ;
516   talloc_free(query);
517   
518   return retval;
519 }
520
521 static NTSTATUS pgsqlsam_add_sam_account ( struct pdb_methods *methods, SAM_ACCOUNT *newpwd )
522 {
523   return pgsqlsam_replace_sam_account( methods, newpwd, 0 ) ;
524 }
525
526 static NTSTATUS pgsqlsam_update_sam_account ( struct pdb_methods *methods, SAM_ACCOUNT *newpwd )
527 {
528   return pgsqlsam_replace_sam_account( methods, newpwd, 1 ) ;
529 }
530
531 static NTSTATUS pgsqlsam_init ( struct pdb_context *pdb_context, struct pdb_methods **pdb_method, const char *location )
532 {
533   NTSTATUS nt_status ;
534   struct pdb_pgsql_data *data ;
535   
536   if ( !pdb_context )
537   {
538     DEBUG( 0, ("invalid pdb_methods specified\n") ) ;
539     return NT_STATUS_UNSUCCESSFUL;
540   }
541   
542   the_pdb_context = pdb_context;
543
544   if (!NT_STATUS_IS_OK
545     (nt_status = make_pdb_methods(pdb_context->mem_ctx, pdb_method))) {
546     return nt_status;
547   }
548   
549   (*pdb_method)->name               = "pgsqlsam" ;
550   
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 ;
559   
560   data = talloc( pdb_context->mem_ctx, struct pdb_pgsql_data ) ;
561   (*pdb_method)->private_data = data ;
562
563   data->master_handle = NULL;
564   data->handle = NULL;
565   data->pwent  = NULL ;
566
567   if ( !location )
568   {
569     DEBUG( 0, ("No identifier specified. Check the Samba HOWTO Collection for details\n") ) ;
570     return NT_STATUS_INVALID_PARAMETER;
571   }
572
573   data->location = smb_xstrdup( location ) ;
574
575   if(!sql_account_config_valid(data->location)) {
576           return NT_STATUS_INVALID_PARAMETER;
577   }
578
579   DEBUG
580   (
581     1,
582     (
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 )
588     )
589   ) ;
590
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 );
597
598   DEBUG( 5, ("Pgsql module intialized\n") ) ;
599   return NT_STATUS_OK;
600 }
601
602 NTSTATUS pdb_pgsql_init(void) 
603 {
604   return smb_register_passdb( PASSDB_INTERFACE_VERSION, "pgsql", pgsqlsam_init ) ;
605 }