first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[kai/samba.git] / source / 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 #include "includes.h"
21
22 extern int DEBUGLEVEL;
23
24 BOOL global_machine_password_needs_changing = False;
25
26 /***************************************************************
27  Lock an fd. Abandon after waitsecs seconds.
28 ****************************************************************/
29
30 BOOL pw_file_lock(int fd, int type, int secs, int *plock_depth)
31 {
32   if (fd < 0)
33     return False;
34
35   if(*plock_depth == 0) {
36     if (!do_file_lock(fd, secs, type)) {
37       DEBUG(10,("pw_file_lock: locking file failed, error = %s.\n",
38                  strerror(errno)));
39       return False;
40     }
41   }
42
43   (*plock_depth)++;
44
45   return True;
46 }
47
48 /***************************************************************
49  Unlock an fd. Abandon after waitsecs seconds.
50 ****************************************************************/
51
52 BOOL pw_file_unlock(int fd, int *plock_depth)
53 {
54   BOOL ret=True;
55
56   if(*plock_depth == 1)
57     ret = do_file_lock(fd, 5, F_UNLCK);
58
59   if (*plock_depth > 0)
60     (*plock_depth)--;
61
62   if(!ret)
63     DEBUG(10,("pw_file_unlock: unlocking file failed, error = %s.\n",
64                  strerror(errno)));
65   return ret;
66 }
67
68 static int mach_passwd_lock_depth;
69 static FILE *mach_passwd_fp;
70
71 /************************************************************************
72  Routine to get the name for a trust account file.
73 ************************************************************************/
74
75 static void get_trust_account_file_name( char *domain, char *name, char *mac_file)
76 {
77   unsigned int mac_file_len;
78   char *p;
79
80   pstrcpy(mac_file, lp_smb_passwd_file());
81   p = strrchr(mac_file, '/');
82   if(p != NULL)
83     *++p = '\0';
84
85   mac_file_len = strlen(mac_file);
86
87   if ((int)(sizeof(pstring) - mac_file_len - strlen(domain) - strlen(name) - 6) < 0)
88   {
89     DEBUG(0,("trust_password_lock: path %s too long to add trust details.\n",
90               mac_file));
91     return;
92   }
93
94   pstrcat(mac_file, domain);
95   pstrcat(mac_file, ".");
96   pstrcat(mac_file, name);
97   pstrcat(mac_file, ".mac");
98 }
99  
100 /************************************************************************
101  Routine to lock the trust account password file for a domain.
102 ************************************************************************/
103
104 BOOL trust_password_lock( char *domain, char *name, BOOL update)
105 {
106   pstring mac_file;
107
108   if(mach_passwd_lock_depth == 0) {
109
110     get_trust_account_file_name( domain, name, mac_file);
111
112     if((mach_passwd_fp = sys_fopen(mac_file, "r+b")) == NULL) {
113       if(errno == ENOENT && update) {
114         mach_passwd_fp = sys_fopen(mac_file, "w+b");
115       }
116
117       if(mach_passwd_fp == NULL) {
118         DEBUG(0,("trust_password_lock: cannot open file %s - Error was %s.\n",
119               mac_file, strerror(errno) ));
120         return False;
121       }
122     }
123
124     chmod(mac_file, 0600);
125
126     if(!pw_file_lock(fileno(mach_passwd_fp), (update ? F_WRLCK : F_RDLCK), 
127                                       60, &mach_passwd_lock_depth))
128     {
129       DEBUG(0,("trust_password_lock: cannot lock file %s\n", mac_file));
130       fclose(mach_passwd_fp);
131       return False;
132     }
133
134   }
135
136   return True;
137 }
138
139 /************************************************************************
140  Routine to unlock the trust account password file for a domain.
141 ************************************************************************/
142
143 BOOL trust_password_unlock(void)
144 {
145   BOOL ret = pw_file_unlock(fileno(mach_passwd_fp), &mach_passwd_lock_depth);
146   if(mach_passwd_lock_depth == 0)
147     fclose(mach_passwd_fp);
148   return ret;
149 }
150
151 /************************************************************************
152  Routine to delete the trust account password file for a domain.
153 ************************************************************************/
154
155 BOOL trust_password_delete( char *domain, char *name )
156 {
157   pstring mac_file;
158
159   get_trust_account_file_name( domain, name, mac_file);
160   return (unlink( mac_file ) == 0);
161 }
162
163 /************************************************************************
164  Routine to get the trust account password for a domain.
165  The user of this function must have locked the trust password file.
166 ************************************************************************/
167
168 BOOL get_trust_account_password( unsigned char *ret_pwd, time_t *pass_last_set_time)
169 {
170   char linebuf[256];
171   char *p;
172   int i;
173
174   linebuf[0] = '\0';
175
176   *pass_last_set_time = (time_t)0;
177   memset(ret_pwd, '\0', 16);
178
179   if(sys_fseek( mach_passwd_fp, (SMB_OFF_T)0, SEEK_SET) == -1) {
180     DEBUG(0,("get_trust_account_password: Failed to seek to start of file. Error was %s.\n",
181               strerror(errno) ));
182     return False;
183   } 
184
185   fgets(linebuf, sizeof(linebuf), mach_passwd_fp);
186   if(ferror(mach_passwd_fp)) {
187     DEBUG(0,("get_trust_account_password: Failed to read password. Error was %s.\n",
188               strerror(errno) ));
189     return False;
190   }
191
192   if(linebuf[strlen(linebuf)-1] == '\n')
193     linebuf[strlen(linebuf)-1] = '\0';
194
195   /*
196    * The length of the line read
197    * must be 45 bytes ( <---XXXX 32 bytes-->:TLC-12345678
198    */
199
200   if(strlen(linebuf) != 45) {
201     DEBUG(0,("get_trust_account_password: Malformed trust password file (wrong length \
202 - was %d, should be 45).\n", (int)strlen(linebuf)));
203 #ifdef DEBUG_PASSWORD
204     DEBUG(100,("get_trust_account_password: line = |%s|\n", linebuf));
205 #endif
206     return False;
207   }
208
209   /*
210    * Get the hex password.
211    */
212
213   if (!pdb_gethexpwd((char *)linebuf, ret_pwd) || linebuf[32] != ':' || 
214          strncmp(&linebuf[33], "TLC-", 4)) {
215     DEBUG(0,("get_trust_account_password: Malformed trust password file (incorrect format).\n"));
216 #ifdef DEBUG_PASSWORD
217     DEBUG(100,("get_trust_account_password: line = |%s|\n", linebuf));
218 #endif
219     return False;
220   }
221
222   /*
223    * Get the last changed time.
224    */
225   p = &linebuf[37];
226
227   for(i = 0; i < 8; i++) {
228     if(p[i] == '\0' || !isxdigit((int)p[i])) {
229       DEBUG(0,("get_trust_account_password: Malformed trust password file (no timestamp).\n"));
230 #ifdef DEBUG_PASSWORD
231       DEBUG(100,("get_trust_account_password: line = |%s|\n", linebuf));
232 #endif
233       return False;
234     }
235   }
236
237   /*
238    * p points at 8 characters of hex digits -
239    * read into a time_t as the seconds since
240    * 1970 that the password was last changed.
241    */
242
243   *pass_last_set_time = (time_t)strtol(p, NULL, 16);
244
245   return True;
246 }
247
248 /************************************************************************
249  Routine to get the trust account password for a domain.
250  The user of this function must have locked the trust password file.
251 ************************************************************************/
252
253 BOOL set_trust_account_password( unsigned char *md4_new_pwd)
254 {
255   char linebuf[64];
256   int i;
257
258   if(sys_fseek( mach_passwd_fp, (SMB_OFF_T)0, SEEK_SET) == -1) {
259     DEBUG(0,("set_trust_account_password: Failed to seek to start of file. Error was %s.\n",
260               strerror(errno) ));
261     return False;
262   } 
263
264   for (i = 0; i < 16; i++)
265     slprintf(&linebuf[(i*2)], sizeof(linebuf) -  (i*2) - 1, "%02X", md4_new_pwd[i]);
266
267   slprintf(&linebuf[32], 32, ":TLC-%08X\n", (unsigned)time(NULL));
268
269   if(fwrite( linebuf, 1, 46, mach_passwd_fp)!= 46) {
270     DEBUG(0,("set_trust_account_password: Failed to write file. Warning - the trust \
271 account is now invalid. Please recreate. Error was %s.\n", strerror(errno) ));
272     return False;
273   }
274
275   fflush(mach_passwd_fp);
276   return True;
277 }
278
279 BOOL trust_get_passwd( unsigned char trust_passwd[16], char *domain, char *myname)
280 {
281   time_t lct;
282
283   /*
284    * Get the machine account password.
285    */
286   if(!trust_password_lock( domain, myname, False)) {
287     DEBUG(0,("domain_client_validate: unable to open the machine account password file for \
288 machine %s in domain %s.\n", myname, domain ));
289     return False;
290   }
291
292   if(get_trust_account_password( trust_passwd, &lct) == False) {
293     DEBUG(0,("domain_client_validate: unable to read the machine account password for \
294 machine %s in domain %s.\n", myname, domain ));
295     trust_password_unlock();
296     return False;
297   }
298
299   trust_password_unlock();
300
301   /* 
302    * Here we check the last change time to see if the machine
303    * password needs changing. JRA. 
304    */
305
306   if(time(NULL) > lct + lp_machine_password_timeout())
307   {
308     global_machine_password_needs_changing = True;
309   }
310   return True;
311 }