Changes from APPLIANCE_HEAD:
[ira/wip.git] / source3 / passdb / smbpassfile.c
1 /*
2  * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup
3  * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  * 
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  * 
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 675
17  * Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 /*
21  * This file also contains migration code to move from an old
22  * trust account password file stored in the file :
23  * ${SAMBA_HOME}/private/{domain}.{netbiosname}.mac
24  * into a record stored in the tdb ${SAMBA_HOME}/private/secrets.tdb
25  * database. JRA.
26  */
27
28 #include "includes.h"
29
30 extern int DEBUGLEVEL;
31 extern pstring global_myname;
32
33 BOOL global_machine_password_needs_changing = False;
34
35
36 static int mach_passwd_lock_depth;
37 static FILE *mach_passwd_fp;
38
39 /***************************************************************
40  Lock an fd. Abandon after waitsecs seconds.
41 ****************************************************************/
42
43 static BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
44 {
45   if (fd < 0)
46     return False;
47
48   if(*plock_depth == 0) {
49     if (!do_file_lock(fd, secs, type)) {
50       DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
51                  strerror(errno)));
52       return False;
53     }
54   }
55
56   (*plock_depth)++;
57
58   return True;
59 }
60
61 /***************************************************************
62  Unlock an fd. Abandon after waitsecs seconds.
63 ****************************************************************/
64
65 static BOOL pw_file_unlock(int fd, int *plock_depth)
66 {
67   BOOL ret=True;
68
69   if(*plock_depth == 1)
70     ret = do_file_lock(fd, 5, F_UNLCK);
71
72   if (*plock_depth > 0)
73     (*plock_depth)--;
74
75   if(!ret)
76     DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
77                  strerror(errno)));
78   return ret;
79 }
80
81 /************************************************************************
82  Routine to get the name for an old trust account file.
83 ************************************************************************/
84
85 static void get_trust_account_file_name( char *domain, char *name, char *mac_file)
86 {
87   unsigned int mac_file_len;
88
89   pstrcpy(mac_file, lp_private_dir());
90   if (mac_file[strlen(mac_file)-1] != '/')
91         pstrcat (mac_file, "/");
92
93   mac_file_len = strlen(mac_file);
94
95   if ((int)(sizeof(pstring) - mac_file_len - strlen(domain) - strlen(name) - 6) < 0)
96   {
97     DEBUG(0,("trust_password_lock: path %s too long to add trust details.\n",
98               mac_file));
99     return;
100   }
101
102   pstrcat(mac_file, domain);
103   pstrcat(mac_file, ".");
104   pstrcat(mac_file, name);
105   pstrcat(mac_file, ".mac");
106 }
107  
108 /************************************************************************
109  Routine to lock the old trust account password file for a domain.
110  As this is a function to migrate to the new secrets.tdb, we never
111  create the file here, only open it.
112 ************************************************************************/
113
114 static BOOL trust_password_file_lock(char *domain, char *name)
115 {
116   pstring mac_file;
117
118   if(mach_passwd_lock_depth == 0) {
119     int fd;
120
121     get_trust_account_file_name( domain, name, mac_file);
122
123     if ((fd = sys_open(mac_file, O_RDWR, 0)) == -1)
124       return False;
125
126     if((mach_passwd_fp = fdopen(fd, "w+b")) == NULL) {
127         DEBUG(0,("trust_password_lock: cannot open file %s - Error was %s.\n",
128               mac_file, strerror(errno) ));
129         return False;
130     }
131
132     if(!pw_file_lock(fileno(mach_passwd_fp), F_WRLCK, 60, &mach_passwd_lock_depth)) {
133       DEBUG(0,("trust_password_lock: cannot lock file %s\n", mac_file));
134       fclose(mach_passwd_fp);
135       return False;
136     }
137
138   }
139
140   return True;
141 }
142
143 /************************************************************************
144  Routine to unlock the old trust account password file for a domain.
145 ************************************************************************/
146
147 static BOOL trust_password_file_unlock(void)
148 {
149   BOOL ret = pw_file_unlock(fileno(mach_passwd_fp), &mach_passwd_lock_depth);
150   if(mach_passwd_lock_depth == 0)
151     fclose(mach_passwd_fp);
152   return ret;
153 }
154
155 /************************************************************************
156  Routine to delete the old trust account password file for a domain.
157  Note that this file must be locked as it is truncated before the
158  delete. This is to ensure it only gets deleted by one smbd.
159 ************************************************************************/
160
161 static BOOL trust_password_file_delete( char *domain, char *name )
162 {
163   pstring mac_file;
164   int ret;
165
166   get_trust_account_file_name( domain, name, mac_file);
167   if(sys_ftruncate(fileno(mach_passwd_fp),(SMB_OFF_T)0) == -1) {
168     DEBUG(0,("trust_password_file_delete: Failed to truncate file %s (%s)\n",
169         mac_file, strerror(errno) ));
170   }
171   ret = unlink( mac_file );
172   return (ret != -1);
173 }
174
175 /************************************************************************
176  Routine to get the old trust account password for a domain - to convert
177  to the new secrets.tdb entry.
178  The user of this function must have locked the trust password file.
179 ************************************************************************/
180
181 static BOOL get_trust_account_password_from_file( unsigned char *ret_pwd, time_t *pass_last_set_time)
182 {
183   char linebuf[256];
184   char *p;
185   int i;
186   SMB_STRUCT_STAT st;
187   linebuf[0] = '\0';
188
189   *pass_last_set_time = (time_t)0;
190   memset(ret_pwd, '\0', 16);
191
192   if(sys_fstat(fileno(mach_passwd_fp), &st) == -1) {
193     DEBUG(0,("get_trust_account_password: Failed to stat file. Error was %s.\n",
194               strerror(errno) )); 
195     return False;
196   }
197
198   /*
199    * If size is zero, another smbd has migrated this file
200    * to the secrets.tdb file, and we are in a race condition.
201    * Just ignore the file.
202    */
203
204   if (st.st_size == 0)
205     return False;
206
207   if(sys_fseek( mach_passwd_fp, (SMB_OFF_T)0, SEEK_SET) == -1) {
208     DEBUG(0,("get_trust_account_password: Failed to seek to start of file. Error was %s.\n",
209               strerror(errno) ));
210     return False;
211   } 
212
213   fgets(linebuf, sizeof(linebuf), mach_passwd_fp);
214   if(ferror(mach_passwd_fp)) {
215     DEBUG(0,("get_trust_account_password: Failed to read password. Error was %s.\n",
216               strerror(errno) ));
217     return False;
218   }
219
220   if(linebuf[strlen(linebuf)-1] == '\n')
221     linebuf[strlen(linebuf)-1] = '\0';
222
223   /*
224    * The length of the line read
225    * must be 45 bytes ( <---XXXX 32 bytes-->:TLC-12345678
226    */
227
228   if(strlen(linebuf) != 45) {
229     DEBUG(0,("get_trust_account_password: Malformed trust password file (wrong length \
230 - was %d, should be 45).\n", (int)strlen(linebuf)));
231 #ifdef DEBUG_PASSWORD
232     DEBUG(100,("get_trust_account_password: line = |%s|\n", linebuf));
233 #endif
234     return False;
235   }
236
237   /*
238    * Get the hex password.
239    */
240
241   if (!pdb_gethexpwd((char *)linebuf, ret_pwd) || linebuf[32] != ':' || 
242          strncmp(&linebuf[33], "TLC-", 4)) {
243     DEBUG(0,("get_trust_account_password: Malformed trust password file (incorrect format).\n"));
244 #ifdef DEBUG_PASSWORD
245     DEBUG(100,("get_trust_account_password: line = |%s|\n", linebuf));
246 #endif
247     return False;
248   }
249
250   /*
251    * Get the last changed time.
252    */
253   p = &linebuf[37];
254
255   for(i = 0; i < 8; i++) {
256     if(p[i] == '\0' || !isxdigit((int)p[i])) {
257       DEBUG(0,("get_trust_account_password: Malformed trust password file (no timestamp).\n"));
258 #ifdef DEBUG_PASSWORD
259       DEBUG(100,("get_trust_account_password: line = |%s|\n", linebuf));
260 #endif
261       return False;
262     }
263   }
264
265   /*
266    * p points at 8 characters of hex digits -
267    * read into a time_t as the seconds since
268    * 1970 that the password was last changed.
269    */
270
271   *pass_last_set_time = (time_t)strtol(p, NULL, 16);
272
273   return True;
274 }
275
276 /************************************************************************
277  Migrate an old DOMAIN.MACINE.mac password file to the tdb secrets db.
278 ************************************************************************/
279
280 BOOL migrate_from_old_password_file(char *domain)
281 {
282         struct machine_acct_pass pass;
283
284         if (!trust_password_file_lock(domain, global_myname))
285                 return True;
286
287         if (!get_trust_account_password_from_file( pass.hash, &pass.mod_time)) {
288                 trust_password_file_unlock();
289                 return False;
290         }
291
292         if (!secrets_store(trust_keystr(domain), (void *)&pass, sizeof(pass)))
293                 return False;
294
295         trust_password_file_delete(domain, global_myname);
296         trust_password_file_unlock();
297
298         return True;
299 }