35c82651b7e4e8e45b0c60c374f39c706188c76b
[samba.git] / source3 / passdb / mysqlpass.c
1 /* 
2  *  Unix SMB/Netbios implementation.
3  *  Version 1.9.
4  *  Samba MYSQL SAM Database, by Benjamin Kuit.
5  *  Copyright (C) Benjamin Kuit                     1999,
6  *  Copyright (C) Andrew Tridgell              1992-1999,
7  *  Copyright (C) Luke Kenneth Casson Leighton 1996-1998,
8  *  
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *  
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *  
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #if defined(HAVE_MYSQL_H) || defined(WITH_MYSQLSAM)
25
26 #include "includes.h"
27
28 extern int DEBUGLEVEL;
29
30 #define UNIX_NAME(row)   ((*row)[0])
31 #define UNIX_UID(row)    ((*row)[1])
32 #define NT_NAME(row)     ((*row)[2])
33 #define RID(row)         ((*row)[3])
34 #define LM_HASH(row)     ((*row)[4])
35 #define NT_HASH(row)     ((*row)[5])
36 #define FLAGS(row)       ((*row)[6])
37 #define CHANGE_TIME(row) ((*row)[7])
38
39 static fstring mysql_table = { 0 };
40
41 struct mysql_struct {
42         MYSQL handle;
43         MYSQL_RES *result;
44         uint current_row;
45 };
46 typedef struct mysql_struct mysql_ctrl;
47
48 static char *mysql_retrieve_password(char *passfile)
49 {
50         static fstring pass;
51         static time_t last_checked = (time_t)0;
52         static char pass_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_-+=|~`\\{}[]:;\"'?/>.<,";
53         fstring temppass;
54         FILE *filep;
55         int length;
56
57         DEBUG(5,("%s\n",FUNCTION_MACRO));
58
59         if ( passfile == NULL ) {
60                 pass[0]=0;
61                 return pass;
62         }
63
64         if ( time(NULL) - last_checked <= 60 ) {
65                 return pass;
66         }
67
68         if ( file_modtime(passfile) < last_checked ) {
69                 return pass;
70         }
71
72         filep = sys_fopen(passfile,"r");
73
74         if ( filep == NULL ) {
75                 return pass;
76         }
77
78         memset(temppass,0,sizeof(temppass));
79
80         if ( fgets( temppass, sizeof(temppass)-1, filep) == NULL ) {
81                 fclose(filep);
82                 return pass;
83         }
84
85         fclose(filep);
86
87         length = strspn( temppass, pass_chars );
88         temppass[length<sizeof(temppass)-1?length:sizeof(temppass)-1] = '\0';
89
90         fstrcpy( pass, temppass );
91
92         last_checked = time(NULL);
93
94         return pass;
95 }
96
97 static int mysql_db_connect( MYSQL *handle )
98 {
99         char *password;
100
101         DEBUG(5,("%s\n",FUNCTION_MACRO));
102
103         password = mysql_retrieve_password(lp_mysql_passfile());
104
105         if ( !mysql_connect(handle, lp_mysql_host(), lp_mysql_user(), password) ) {
106                 DEBUG(0,("mysql_connect: %s\n",mysql_error(handle)));
107                 return -1;
108         }
109
110         if ( mysql_select_db( handle, lp_mysql_db()) ) {
111                 DEBUG(0,("mysql_connect: %s\n",mysql_error(handle)));
112                 mysql_close(handle);
113                 return -1;
114         }
115
116         fstrcpy(mysql_table,lp_mysql_table());
117
118         return 0;
119 }
120
121 static int mysql_lock_table( MYSQL *handle, BOOL write_access )
122 {
123         fstring query;
124
125         DEBUG(5,("%s\n",FUNCTION_MACRO));
126
127         slprintf( query, sizeof(query), "lock tables %s %s", mysql_table, write_access==True?"write":"read");
128
129         if ( mysql_query( handle, query ) ) {
130                 DEBUG(0,("Cannot get lock: %s: %s\n",query,mysql_error(handle) ));
131                 return -1;
132         }
133
134         return 0;
135 }
136
137 int mysql_db_lock_connect( MYSQL *handle )
138 {
139
140         DEBUG(5,("%s\n",FUNCTION_MACRO));
141
142         if ( mysql_db_connect( handle ) ) {
143                 return -1;
144         }
145         
146         if ( mysql_lock_table( handle, True ) ) {
147                 mysql_close( handle );
148                 return -1;
149         }
150
151         return 0;
152 }
153
154 static MYSQL_RES *mysql_select_results( MYSQL *handle, char *selection )
155 {
156         MYSQL_RES *result;
157         pstring query;
158         int query_length;
159         char select[] = "select ";
160         char where[] = " where ";
161         char from[] = " from ";
162         char mysql_query_string[] = "unix_name, unix_uid, nt_name, user_rid, smb_passwd, smb_nt_passwd, acct_ctrl, pass_last_set_time";
163
164         DEBUG(5,("%s\n",FUNCTION_MACRO));
165
166         query_length = sizeof( select ) + sizeof( mysql_query_string ) + sizeof(from ) + strlen( mysql_table );
167
168         if ( selection != NULL && *selection != '\0' ) {
169                 query_length += sizeof( where ) + strlen( selection );
170         }
171
172         if ( query_length >= sizeof( query ) ) {
173                 DEBUG(0,("Query string too long\n"));
174                 return NULL;
175         }
176
177         pstrcpy( query, select);
178         pstrcat( query, mysql_query_string );
179         pstrcat( query, from );
180         pstrcat( query, mysql_table );
181
182         if ( selection != NULL && *selection != '\0' ) {
183                 pstrcat( query, where );
184                 pstrcat( query, selection );
185         }
186
187         DEBUG(5,("mysql> %s\n",query));
188         if ( mysql_query( handle, query ) ) {
189                 DEBUG(0,("%s: %s\n", query, mysql_error(handle) ));
190                 return NULL;
191         }
192
193         result = mysql_store_result( handle );
194
195         if ( mysql_num_fields( result ) != 8 ) {
196                 DEBUG(0,("mysql_num_result = %d (!=8)\n",mysql_num_fields( result )));
197                 return NULL;
198         }
199
200         if ( result == NULL ) {
201                 DEBUG(0,("mysql_store_result: %s\n",mysql_error(handle)));
202                 return NULL;
203         }
204
205         return result;
206 }
207
208 void *mysql_startpwent( BOOL update )
209 {
210         mysql_ctrl *mysql;
211
212         DEBUG(5,("%s\n",FUNCTION_MACRO));
213
214         mysql = (mysql_ctrl *)malloc( sizeof(mysql_ctrl) );
215         if ( mysql == NULL ) {
216                 DEBUG(0,("malloc: Out of memory\n"));
217                 return NULL;
218         }
219
220         memset( mysql, 0, sizeof(mysql_ctrl) );
221
222         if ( mysql_db_connect( &mysql->handle ) ) {
223                 return NULL;
224         }
225
226         if ( mysql_lock_table( &mysql->handle, update ) ) {
227                 mysql_close( &mysql->handle );
228                 return NULL;
229         }
230
231         mysql->result = mysql_select_results( &mysql->handle, NULL );
232
233         if ( mysql->result == NULL ) {
234                 mysql_close( &mysql->handle );
235                 return NULL;
236         }
237
238         mysql->current_row = 0;
239
240         return (void*)mysql;
241 }
242
243 void mysql_endpwent( void *ptr )
244 {
245         mysql_ctrl *handle;
246
247         DEBUG(5,("%s\n",FUNCTION_MACRO));
248         handle = (mysql_ctrl *)ptr;
249
250         mysql_free_result( handle->result );
251
252         mysql_close( &handle->handle );
253
254         free( handle );
255 }
256
257 SMB_BIG_UINT mysql_getpwpos(void *vp)
258 {
259
260         DEBUG(5,("%s\n",FUNCTION_MACRO));
261
262         return ((mysql_ctrl *)vp)->current_row;
263 }
264
265 BOOL mysql_setpwpos(void *vp, SMB_BIG_UINT pos)
266 {
267
268         DEBUG(5,("%s\n",FUNCTION_MACRO));
269
270         mysql_data_seek( ((mysql_ctrl*)vp)->result, (uint)pos );
271         ((mysql_ctrl *)vp)->current_row=(uint)pos;
272
273         return True;
274 }
275
276 static void quote_hash( char *target, unsigned char *passwd )
277 {
278         char hex[] = "0123456789ABCDEF";
279         int i;
280
281         DEBUG(5,("%s\n",FUNCTION_MACRO));
282
283         if ( passwd == NULL ) {
284                 fstrcpy(target,"NULL");
285         }
286         else {
287                 target[0]='\'';
288                 for (i=0;i<32;i++) {
289                         target[i+1] = hex[(passwd[i>>1]>>(((~i)&1)<<2))&15];
290                 }
291                 target[33] = '\'';
292                 target[34] = '\0';
293         }
294 }
295
296 static unsigned char *decode_hash( char *hash, unsigned char *buffer )
297 {
298         char hex[] = "0123456789ABCDEF";
299         int pos, v1, v2;
300
301         DEBUG(5,("%s\n",FUNCTION_MACRO));
302
303         if ( hash == NULL ) {
304                 return NULL;
305         }
306
307         for (pos=0;pos<16;pos++) {
308                 for( v1 = 0; v1 < sizeof(hex) && hash[0] != hex[v1]; v1++ );
309                 for( v2 = 0; v2 < sizeof(hex) && hash[1] != hex[v2]; v2++ );
310
311                 if ( v1 == sizeof(hex) || v2 == sizeof(hex) ) {
312                         return NULL;
313                 }
314
315                 buffer[pos] = (v1<<4)|v2;
316                 hash += 2;
317         }
318
319         return buffer;
320 }
321
322 void *mysql_fill_smb_passwd( MYSQL_ROW *row )
323 {
324         static struct smb_passwd pw_buf;
325         static fstring unix_name;
326         static fstring nt_name;
327         static unsigned char smbpwd[16];
328         static unsigned char smbntpwd[16];
329
330         DEBUG(5,("%s\n",FUNCTION_MACRO));
331
332         pwdb_init_smb(&pw_buf);
333
334         fstrcpy( unix_name, UNIX_NAME(row) );
335         pw_buf.unix_name = unix_name;
336         pw_buf.unix_uid = get_number( UNIX_UID(row) );
337
338         if ( NT_NAME(row) != NULL ) {
339                 fstrcpy( nt_name, NT_NAME(row) );
340                 pw_buf.nt_name = nt_name;
341         }
342
343         if ( RID(row) != NULL ) {
344                 pw_buf.user_rid = get_number( RID(row) );
345         }
346
347         pw_buf.smb_passwd = decode_hash( LM_HASH(row), smbpwd );
348         if ( !pw_buf.smb_passwd ) {
349                 DEBUG(4, ("entry invalidated for unix user %s\n", unix_name ));
350                 return NULL;
351         }
352
353         pw_buf.smb_nt_passwd = decode_hash( NT_HASH(row), smbntpwd );
354
355         if ( FLAGS(row) != NULL ) {
356                 pw_buf.acct_ctrl = get_number( FLAGS(row) );
357         }
358
359         if ( pw_buf.acct_ctrl == 0 ) {
360                 pw_buf.acct_ctrl = ACB_NORMAL;
361         }
362
363         pw_buf.pass_last_set_time = get_number( CHANGE_TIME(row) );
364
365         return (void*)&pw_buf;
366 }
367
368 MYSQL_ROW *mysql_getpwent(void *vp)
369 {
370         mysql_ctrl *mysql;
371         static MYSQL_ROW row;
372
373         DEBUG(5,("%s\n",FUNCTION_MACRO));
374
375         mysql = (mysql_ctrl*)vp;
376         row = mysql_fetch_row( mysql->result );
377
378         if ( row == NULL ) {
379                 return NULL;
380         }
381
382         mysql->current_row++;
383
384         return &row;
385 }
386
387 struct smb_passwd *mysql_getsmbpwent(void *vp)
388 {
389
390         DEBUG(5,("%s\n",FUNCTION_MACRO));
391
392         return (struct smb_passwd*)mysql_fill_smb_passwd( mysql_getpwent(vp) );
393 }
394
395 void *mysql_fetch_passwd( void *(*filler)(MYSQL_ROW*), char *where )
396 {
397         void *retval;
398         MYSQL handle;
399         MYSQL_RES *result;
400         MYSQL_ROW row;
401
402         DEBUG(5,("%s\n",FUNCTION_MACRO));
403
404         if ( filler == NULL ) {
405                 return NULL;
406         }
407
408         if ( where == NULL || *where == '\0' ) {
409                 DEBUG(0,("Null or empty query\n"));
410                 return NULL;
411         }
412
413         if ( mysql_db_connect( &handle ) ) {
414                 return NULL;
415         }
416
417         result = mysql_select_results( &handle, where );
418         if ( result == NULL ) {
419                 mysql_close( &handle );
420                 return NULL;
421         }
422
423         row = mysql_fetch_row ( result );
424         if ( row == NULL ) {
425                 mysql_free_result( result );
426                 mysql_close( &handle );
427                 return NULL;
428         }
429
430         if ( DEBUGLEVEL >= 7 ) {
431                 int field;
432                 for (field=0; field< mysql_num_fields( result ); field++ ) {
433                         DEBUG(7,(" row[%d] = \"%s\"\n",field,row[field]?row[field]:"NULL"));
434                 }
435         }
436
437         retval = (*filler)( &row );
438
439         mysql_free_result( result );
440         mysql_close( &handle );
441
442         return retval;
443 }
444
445 void *mysql_getpwuid(void *(*filler)(MYSQL_ROW *), uid_t uid)
446 {
447         fstring where;
448
449         DEBUG(5,("%s\n",FUNCTION_MACRO));
450
451         slprintf( where, sizeof(where), "unix_uid=%lu", uid);
452
453         return mysql_fetch_passwd(filler,where);
454 }
455
456 struct smb_passwd *mysql_getsmbpwuid(uid_t uid)
457 {
458
459         DEBUG(5,("%s\n",FUNCTION_MACRO));
460
461         return (struct smb_passwd *)mysql_getpwuid( mysql_fill_smb_passwd, uid );
462 }
463
464 void *mysql_getpwnam(void *(*filler)(MYSQL_ROW *), char *field, const char *name)
465 {
466         fstring where;
467         char format[] = "%s='%s'";
468
469         DEBUG(5,("%s\n",FUNCTION_MACRO));
470
471         if ( filler == NULL ) {
472                 DEBUG(0,("Empty fill opteration\n"));
473                 return NULL;
474         }
475
476         if ( field == NULL || *field == '\0' ) {
477                 DEBUG(0,("Empty or NULL field name\n"));
478                 return NULL;
479         }
480
481         if ( name == NULL || *name == '\0' ) {
482                 DEBUG(0,("Empty or NULL query\n"));
483                 return NULL;
484         }
485
486         if ( sizeof(format) + strlen(name) + strlen(field) > sizeof(where) ) {
487                 DEBUG(0,("Query string too long\n"));
488                 return NULL;
489         }
490
491         slprintf(where, sizeof( where ), format, field, name );
492
493         return mysql_fetch_passwd( filler, where );
494 }
495
496 struct smb_passwd *mysql_getsmbpwnam(const char *unix_name)
497 {
498         DEBUG(5,("%s\n",FUNCTION_MACRO));
499
500         return mysql_getpwnam( mysql_fill_smb_passwd, "unix_name", unix_name );
501 }
502
503 static void quote_string(char *target, char *string)
504 {
505         DEBUG(5,("%s\n",FUNCTION_MACRO));
506
507         if ( string == NULL ) {
508                 fstrcpy( target, "NULL" );
509         }
510         else {
511                 target[0] = '\'';
512                 safe_strcpy(&target[1],string,sizeof(fstring)-2);
513                 safe_strcpy(&target[strlen(target)],"'",2);
514         }
515 }
516
517 BOOL mysql_del_smb( MYSQL *handle, char *unix_name )
518 {
519         pstring query;
520         char format[] = "delete from %s where unix_name='%s'";
521
522         DEBUG(5,("%s\n",FUNCTION_MACRO));
523
524         if (strlen( format ) + strlen(mysql_table) + strlen(unix_name)) {
525                 return False;
526         }
527
528         slprintf( query, sizeof(query), format, mysql_table, unix_name);
529
530         if ( mysql_query( handle, query ) ) {
531                 DEBUG(0,("%s: %s\n", query, mysql_error(handle) ));
532                 return False;
533         }
534
535         return True;
536 }
537
538 BOOL mysql_add_smb( MYSQL *handle, struct smb_passwd *smb )
539 {
540         pstring query;
541         char format[] = "insert into %s (unix_name, unix_uid) values ( '%s', %lu )";
542
543         DEBUG(5,("%s\n",FUNCTION_MACRO));
544
545         if ( strlen(format) + strlen(mysql_table) + strlen(smb->unix_name) + 10 > sizeof(query) ) {
546                 DEBUG(0,("Query too long\n"));
547                 return False;
548         }
549         
550         slprintf( query, sizeof(query), "insert into %s (unix_name,unix_uid) values ('%s', %lu)", mysql_table, smb->unix_name, smb->unix_uid);
551
552         if ( mysql_query( handle, query ) ) {
553                 DEBUG(0,("%s: %s\n",query,mysql_error(handle) ));
554                 return False;
555         }
556
557         return True;
558 }
559
560 BOOL mysql_mod_smb( MYSQL *handle, struct smb_passwd *smb, BOOL override )
561 {
562         pstring query;
563         fstring smb_passwd;
564         fstring smb_nt_passwd;
565         fstring nt_name;
566
567         char format[] = "update %s set nt_name=%s, user_rid=%lu, smb_passwd=%s, smb_nt_passwd=%s, acct_ctrl=%u, pass_last_set_time=unix_timestamp() where unix_name='%s'";
568         char extra[] = " and not ISNULL(smb_passwd)";
569
570         DEBUG(5,("%s\n",FUNCTION_MACRO));
571
572         if ( strlen(format) + 2*20 + 3*10 + 2*32 + strlen(mysql_table) >= sizeof( query ) + strlen( extra ) ) {
573                 DEBUG(0,("Query string too long\n"));
574                 return False;
575         }
576         
577         quote_hash(smb_passwd, smb->smb_passwd);
578         quote_hash(smb_nt_passwd, smb->smb_nt_passwd);
579
580         quote_string(nt_name, smb->nt_name);
581
582         slprintf( query, sizeof(query), format, mysql_table, nt_name, (long unsigned)smb->user_rid, smb_passwd, smb_nt_passwd, smb->acct_ctrl, smb->unix_name);
583
584         if ( override != True ) {
585                 pstrcat( query, extra );
586         }
587
588         if ( mysql_query( handle, query ) ) {
589                 DEBUG(0,("%s: %s\n",query,mysql_error(handle) ));
590                 return False;
591         }
592
593         if ( mysql_affected_rows( handle ) < 1 ) {
594                 DEBUG(3,("No entries changed\n"));
595                 return False;
596         }
597
598         return True;
599 }
600
601 BOOL mysql_add_smbpwd_entry(struct smb_passwd *smb)
602 {
603         MYSQL handle;
604
605         DEBUG(5,("%s\n",FUNCTION_MACRO));
606
607         if ( smb == NULL ) {
608                 return False;
609         }
610
611         if ( mysql_db_lock_connect( &handle ) ) {
612                 return False;
613         }
614
615         if ( !mysql_add_smb( &handle, smb ) ) {
616                 mysql_close( &handle );
617                 return False;
618         }
619
620         if ( !mysql_mod_smb( &handle, smb, True ) ) {
621                 mysql_del_smb( &handle, smb->unix_name );
622                 mysql_close( &handle );
623                 return False;
624         }
625
626         mysql_close(&handle);
627         return True;
628 }
629
630 BOOL mysql_mod_smbpwd_entry(struct smb_passwd *smb, BOOL override)
631 {
632         MYSQL handle;
633
634         DEBUG(5,("%s\n",FUNCTION_MACRO));
635
636         if ( smb == NULL ) {
637                 return False;
638         }
639
640         if ( mysql_db_lock_connect( &handle ) ) {
641                 return False;
642         }
643
644         if ( !mysql_mod_smb( &handle, smb, override ) ) {
645                 mysql_close(&handle);
646                 return False;
647         }
648
649         mysql_close(&handle);
650         return True;
651 }
652
653 static struct smb_passdb_ops mysql_ops = {
654         mysql_startpwent,
655         mysql_endpwent,
656         mysql_getpwpos,
657         mysql_setpwpos,
658         mysql_getsmbpwnam,
659         mysql_getsmbpwuid,
660         mysql_getsmbpwent,
661         mysql_add_smbpwd_entry,
662         mysql_mod_smbpwd_entry
663 };
664
665 struct smb_passdb_ops *mysql_initialise_password_db(void)
666 {
667         (void*)mysql_retrieve_password(NULL);
668         return &mysql_ops;
669 }
670
671 #else
672         void mysql_dummy_smb_function(void) { }
673 #endif