Benjamin Kuit's latest mysql mods. issue with "make proto" needs to be
[samba.git] / source3 / script / mysql_convert.pl
1 #!/bin/env perl
2 #
3 #  MYSQL Convert - Creates and initialises mysql tables for use by samba
4 #
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 #  Converts smbpasswd files into MySQL tables.
25 #  Can understand Samba 1.19 and Samba 2.0 file formats.
26 #  Assumes table structure:
27 #           unix_name            char(20) not null,
28 #           unix_uid             int(10)  unsigned not null,
29 #           nt_name              char(20) not null,
30 #           user_rid             int(10)  unsigned not null,
31 #           smb_passwd           char(32),
32 #           smb_nt_passwd        char(32),
33 #           acct_ctrl            int(10) unsigned not null,
34 #           pass_last_set_time   int(10) unsigned not null,
35 #           unique (unix_name),
36 #           unique (unix_uid)
37 #  When given the --create option, mysql_convert will generate this
38 #  statement.
39
40 #  To move from flat file smbpasswd directly into a mysql table:
41 #
42 #  mysql_convert.pl --db=samba --table=smbpasswd --user=samba --create --infile=smbpasswd
43 #
44 #  Assumes mysql server on localhost, use --host if otherwise.
45 #  To convert back to flat file:
46 #
47 #  mysql_convert.pl --db=samba --table=smbpasswd --user=samba --outfile=smbpasswd
48 #
49 #  If smbpasswd file already exists, use --file=append or --file=trash
50 #  to determine whether to append or over-right the file.
51 #
52 #  In converting from NT Server PDC to Samba PDC:
53 #  Run pwdump on NT Server to generate an smbpasswd file (Samba 1.19 format),
54 #  called say NTpasslist.
55 #  then:
56 #
57 #  mysql_convert.pl --db=samba --table=smbpasswd --user=samba --infile=NTpasslist --create --check
58 #
59 #  The --check option will change the unix_uid field to the real uid
60 #  value of respective users, also filter out users that dont exist on
61 #  the system.
62 #
63 #  If dont have mysql perl module:
64 #
65 #  mysql_convert.pl --table=smbpasswd --infile=NTpasslist --outfile=mysql.txt
66 #
67 #  Then use the mysql client:
68 #
69 #  mysql -u samba < mysql.txt
70 #
71
72 $ACB_DISABLED=0x0001;
73 $ACB_HOMDIRREQ=0x0002;
74 $ACB_PWNOTREQ=0x0004;
75 $ACB_TEMPDUP=0x0008;
76 $ACB_NORMAL=0x0010;
77 $ACB_MNS=0x0020;
78 $ACB_DOMTRUST=0x0040;
79 $ACB_WSTRUST=0x0080;
80 $ACB_SVRTRUST=0x0100;
81 $ACB_PWNOEXP=0x0200;
82 $ACB_AUTOLOCK=0x0400;
83
84 sub getoptionval {
85         my ($option) = @_;
86
87         my ($value) = ($option =~ /^[^=]+=\s*(\S.*\S)\s*$/ );
88
89         return $value;
90 }
91
92 sub usage {
93
94 print <<EOUSAGE;
95 $0 [options]
96 options:
97    --infile=<filename>          # smbpasswd style file to read entries from
98    --outfile=<filename>         # file to dump results to, format depending
99                                 #   on --infile:
100                                 # With --infile: Dump mysql script queries
101                                 # Without --infile: Dump smbpasswd format
102                                 #                from reading mysql database
103    --host=<hostname>            # Mysql Server name (default: localhost)
104    --db=<database>              # Mysql Database name
105    --user=<user>                # Mysql User
106    --password[=<password>]      # Mysql password for --user
107    --table=<table>              # Mysql table
108    --create                     # Generate 'create table' query
109    --file=[trash|append]        # Action to take if --outfile file exists
110    --check                      # Do not alter or skip bad uids
111
112 EOUSAGE
113 exit 0;
114 }
115
116 sub getpass {
117         my($prompt)=@_;
118         my($ret);
119
120         print $prompt;
121         system "stty -echo";
122         chomp($ret=<STDIN>);
123         system "stty echo";
124         print "\n";
125         $ret;
126 }
127
128 sub next_entry {
129         my ($name,$uid,$lm,$nt,$f,$lct) = ();
130
131         $name="";
132         if ( not $infile ) {
133                 ($name,$uid,$lm,$nt,$f,$lct) = $mysqlquery->fetchrow();
134         }
135         else {
136                 my $line=<INFILE>;
137
138                 return () if ( not $line );
139
140                 chomp($line);
141
142                 next if ( $line !~ /^[^: ]+:\d+:/ );
143
144                 ($name,$uid,$lm,$nt,$f,$lct) = split(/:/,$line);
145
146                 if ( $lct =~ /^LCT-/ ) {
147                         # New Format smbpasswd file
148                         my $flags=0;
149
150                         $flags |= $ACB_PWNOTREQ if ( $f =~ /N/ );
151                         $flags |= $ACB_DISABLED if ( $f =~ /D/ );
152                         $flags |= $ACB_HOMDIRREQ if ( $f =~ /H/ );
153                         $flags |= $ACB_TEMPDUP if ( $f =~ /T/ );
154                         $flags |= $ACB_NORMAL if ( $f =~ /U/ );
155                         $flags |= $ACB_MNS if ( $f =~ /M/ );
156                         $flags |= $ACB_WSTRUST if ( $f =~ /W/ );
157                         $flags |= $ACB_SVRTRUST if ( $f =~ /S/ );
158                         $flags |= $ACB_AUTOLOCK if ( $f =~ /L/ );
159                         $flags |= $ACB_PWNOEXP if ( $f =~ /X/ );
160                         $flags |= $ACB_DOMTRUST if ( $f =~ /I/ );
161
162                         $f = $flags;
163
164                         $f = $ACB_NORMAL if ( not $f );
165
166                         $lct =~ s/LCT-//;
167                         $lct = (unpack("L",pack("H8",$lct)))[0];
168                 }
169                 else {
170                         # Old Format smbpasswd file
171                         $f = 0;
172                         $lct = time();
173                         if ( $lm =~ /^NO PASS/ ) {
174                                 $f |= $ACB_PWNOTREQ;
175                                 $lm = "";
176                                 $nt = "";
177                         }
178                         elsif ( $lm =~ /^XX/ ) {
179                                 $f |= $ACB_DISABLED;
180
181                                 $lm = "";
182                                 $nt = "";
183                         }
184
185                         if ( $name =~ /\$$/ ) {
186                                 $f |= $ACB_WSTRUST;
187                         }
188
189                         $f = $ACB_NORMAL if ( not $f );
190                 }
191         }
192         return () if ( not $name );
193         ($name,$uid,$lm,$nt,$f,$lct);
194 }
195
196 sub do_query {
197         my ( $query ) = @_;
198
199         chomp($query);
200         if ( $outfile ) {
201                 print OUTFILE "$query;\n";
202         }
203         else {
204                 if ( not $mysqldb->query($query) ) {
205                         print "$query: $Mysql::db_errstr\n";
206                 }
207         }
208 }
209
210 sub do_file {
211         my ($file,$name,$uid,$lm,$nt,$f,$lct)=@_;
212
213         my $strings = "";
214
215         $strings .= "N" if ( $f & $ACB_PWNOTREQ );
216         $strings .= "D" if ( $f & $ACB_DISABLED );
217         $strings .= "H" if ( $f & $ACB_HOMDIRREQ );
218         $strings .= "T" if ( $f & $ACB_TEMPDUP );
219         $strings .= "U" if ( $f & $ACB_NORMAL );
220         $strings .= "M" if ( $f & $ACB_MNS );
221         $strings .= "W" if ( $f & $ACB_WSTRUST );
222         $strings .= "S" if ( $f & $ACB_SVRTRUST );
223         $strings .= "L" if ( $f & $ACB_AUTOLOCK );
224         $strings .= "X" if ( $f & $ACB_PWNOEXP );
225         $strings .= "I" if ( $f & $ACB_DOMTRUST );
226
227         $f = sprintf( "[%-11s]", $strings );
228
229         $lct=uc("LCT-".(unpack("H8",pack("L","$lct")))[0]);
230
231         $lm = "X"x32 if ( not $lm );
232         $nt = "X"x32 if ( not $nt );
233
234         print $file "$name:$uid:$lm:$nt:$f:$lct\n";
235 }
236
237 $dbhost = "localhost";
238
239 for $option ( @ARGV ) {
240         if ( $option =~ /--outfile=/ ) {
241                 $outfile = getoptionval($option);
242         }
243         elsif ( $option =~ /--infile=/ ) {
244                 $infile = getoptionval($option);
245         }
246         elsif ( $option =~ /--db=/ ) {
247                 $dbname = getoptionval($option);
248         }
249         elsif ( $option =~ /--user=/ ) {
250                 $dbuser = getoptionval($option);
251         }
252         elsif ( $option =~ /--host=/ ) {
253                 $dbhost = getoptionval($option);
254         }
255         elsif ( $option =~ /--password/ ) {
256                 $dbpasswd = getoptionval($option);
257                 $need_password = "yes"
258         }
259         elsif ( $option =~ /--table=/ ) {
260                 $dbtable = getoptionval($option);
261         }
262         elsif ( $option =~ /--create/ ) {
263                 $create_table = "yes";
264         }
265         elsif ( $option =~ /--file=/ ) {
266                 $file_action = getoptionval($option);
267         }
268         elsif ( $option =~ /--check/ ) {
269                 $check = "yes";
270         }
271         else {
272                 print "Unknown option: $option\n";
273                 $unknown = "yes";
274         }
275 }
276
277 &usage if ( $unknown eq "yes" );
278
279 if ( ( not $infile ) && ( not $outfile ) && ( $create_table ne "yes" ) ) {
280         print "Need file to read from or write to\n";
281         &usage;
282 }
283 elsif ( $infile && $outfile ) {
284         if ( not $dbtable ) {
285                 print "Need --table to create queries\n";
286                 exit 1;
287         }
288
289         # Reading a smbpasswd file, dumping queries into an file which
290         # can be used for a mysql script
291         # --db* options are ignored.
292
293         $ignored = "";
294         $ignored .= " --db" if ( $dbname );
295         $ignored .= " --user" if ( $dbuser );
296         $ignored .= " --password" if ( $dbuser );
297
298         if ( $ignored ) {
299                 print "Ignoring options: $ignored\n";
300         }
301 }
302 elsif ( (not $dbname) || (not $dbtable) || (not $dbuser) ) {
303         print "Missing database particulars:\n";
304         print "  --db=??\n" if ( not $dbname );
305         print "  --user=??\n" if ( not $dbuser );
306         print "  --table=??\n" if ( not $dbtable );
307         &usage;
308 }
309 else {
310         use Mysql;
311
312         if ( ($need_password eq "yes") && ( not $dbpasswd )) {
313                 $dbpasswd = getpass("Enter MySQL password for $dbuser: ");
314         }
315         $mysqldb = Connect Mysql($dbhost,$dbname,$dbuser,$dbpasswd);
316
317         if ( not $mysqldb ) {
318                 print "Cannot connect to database: $Mysql::db_errstr\n";
319                 exit 1;
320         }
321
322         if ( $outfile ) {
323                 $mysqlquery = $mysqldb->query("select unix_name,unix_uid,smb_passwd,smb_nt_passwd,acct_ctrl,pass_last_set_time from $dbtable");
324
325                 if ( not $mysqlquery ) {
326                         print "MySQL Query failed: $Mysql::db_errstr\n";
327                         exit 1;
328                 }
329         }
330 }
331
332 if ( $create_table eq "yes" ) {
333         $create_table_query=<<EOSQL;
334 create table $dbtable (
335 unix_name            char(20) not null,
336 unix_uid             int(10)  unsigned not null,
337 nt_name              char(20) not null,
338 user_rid             int(10)  unsigned not null,
339 smb_passwd           char(32),
340 smb_nt_passwd        char(32),
341 acct_ctrl            int(10) unsigned not null,
342 pass_last_set_time   int(10) unsigned not null,
343 unique (unix_name),
344 unique (unix_uid)
345 )
346 EOSQL
347         print "$create_table_query\n";
348 }
349 if ( $infile ) {
350         if ( not open(INFILE,$infile) ) {
351                 print "$infile: $!\n";
352                 exit 1;
353         }
354 }
355
356 if ( $outfile ) {
357         if ( ! -f $outfile ) {
358                 $open_string=">$outfile";
359         }
360         elsif ( not $file_action ) {
361                 print "File $outfile exists:\n";
362                 print "Please use --file=[trash|append] option to determine destiny of file\n";
363                 exit 1;
364         }
365         elsif ( $file_action eq "append" ) {
366                 $open_string = ">>$outfile";
367         }
368         else {
369                 $open_string = ">$outfile";
370         }
371
372         if ( not open(OUTFILE,$open_string) ) {
373                 print "$outfile: $!\n";
374                 exit 1;
375         }
376 }
377
378 do_query($create_table_query) if ( $create_table_query );
379
380 $linenum=1;
381 while (($name,$uid,$lm,$nt,$f,$lct)=next_entry()) {
382         
383         $| = 1;
384         print "\r$linenum ";
385         $linenum++;
386
387         $nuid = "";
388
389         $nuid = (getpwnam(lc($name)))[2];
390
391         if ( $check ) {
392                 if ( not $nuid ) {
393                         # print "Removing $name: Does not exist\n";
394                         push(@removed,[$name,$uid,$lm,$nt,$f,$lct]);
395                         next;
396                 }
397                 else {
398                         # print "Changing uid of $name\n";
399                         $uid = $nuid;
400                 }
401         }
402
403         if ( $infile ) {
404                 if ( $lm ) {
405                         $lm = "'$lm'";
406                 }
407                 else {
408                         $lm = "NULL";
409                 }
410                 if ( $nt ) {
411                         $nt = "'$nt'";
412                 }
413                 else {
414                         $nt = "NULL";
415                 }
416                 $rid=(4*$uid)+1000;
417                 do_query("insert into $dbtable (unix_name,unix_uid,smb_passwd,smb_nt_passwd,acct_ctrl,pass_last_set_time,nt_name,user_rid) values ('$name',$uid,$lm,$nt,$f,$lct,'$name',$rid)");
418         }
419         else {
420                 do_file(OUTFILE,$name,$uid,$lm,$nt,$f,$lct);
421         }
422 }
423
424 if ( @removed ) {
425         print "\n\nIgnored entries because usernames do not exist\n";
426         foreach $line ( @removed ) {
427                 do_file(STDOUT,@{ $line });
428         }
429 }
430
431 close (OUTFILE) if ( $outfile );
432 close (INFILE) if ( $infile );
433 print "\n";