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
22extern int DEBUGLEVEL;
23
24BOOL global_machine_password_needs_changing = False;
25
26/***************************************************************
27 Lock an fd. Abandon after waitsecs seconds.
28****************************************************************/
29
30BOOL 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
52BOOL 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
68static int mach_passwd_lock_depth;
69static FILE *mach_passwd_fp;
70
71/************************************************************************
72 Routine to get the name for a trust account file.
73************************************************************************/
74
75static 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
104BOOL 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
143BOOL 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
155BOOL 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
168BOOL 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
253BOOL 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 \
271account 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
279BOOL 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 \
288machine %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 \
294machine %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}
312