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