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