r9762: Add support for reading good old smbpasswd files
[sfrench/samba-autobuild/.git] / source4 / lib / samba3 / smbpasswd.c
1 /* 
2    Unix SMB/CIFS implementation.
3    smbpasswd file format routines
4
5    Copyright (C) Andrew Tridgell 1992-1998 
6    Modified by Jeremy Allison 1995.
7    Modified by Gerald (Jerry) Carter 2000-2001
8    Copyright (C) Tim Potter 2001
9    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
10    Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2005
11    
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 2 of the License, or
15    (at your option) any later version.
16    
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21    
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27 /*! \file lib/smbpasswd.c
28
29    The smbpasswd file is used to store encrypted passwords in a similar
30    fashion to the /etc/passwd file.  The format is colon separated fields
31    with one user per line like so:
32
33    <username>:<uid>:<lanman hash>:<nt hash>:<acb info>:<last change time>
34
35    The username and uid must correspond to an entry in the /etc/passwd
36    file.  The lanman and nt password hashes are 32 hex digits corresponding
37    to the 16-byte lanman and nt hashes respectively.  
38
39    The password last change time is stored as a string of the format
40    LCD-<change time> where the change time is expressed as an 
41
42    'N'    No password
43    'D'    Disabled
44    'H'    Homedir required
45    'T'    Temp account.
46    'U'    User account (normal) 
47    'M'    MNS logon user account - what is this ? 
48    'W'    Workstation account
49    'S'    Server account 
50    'L'    Locked account
51    'X'    No Xpiry on password 
52    'I'    Interdomain trust account
53
54 */
55
56 #include "includes.h"
57 #include "librpc/gen_ndr/ndr_samr.h"
58 #include "lib/samba3/samba3.h"
59 #include "system/iconv.h"
60
61 /*! Convert 32 hex characters into a 16 byte array. */
62
63 struct samr_Password *smbpasswd_gethexpwd(TALLOC_CTX *mem_ctx, const char *p)
64 {
65         int i;
66         unsigned char   lonybble, hinybble;
67         const char     *hexchars = "0123456789ABCDEF";
68         const char     *p1, *p2;
69         struct samr_Password *pwd = talloc(mem_ctx, struct samr_Password);
70
71         if (!p) return NULL;
72         
73         for (i = 0; i < (sizeof(pwd->hash) * 2); i += 2)
74         {
75                 hinybble = toupper(p[i]);
76                 lonybble = toupper(p[i + 1]);
77                 
78                 p1 = strchr_m(hexchars, hinybble);
79                 p2 = strchr_m(hexchars, lonybble);
80                 
81                 if (!p1 || !p2)
82                 {
83                         return (False);
84                 }
85                 
86                 hinybble = PTR_DIFF(p1, hexchars);
87                 lonybble = PTR_DIFF(p2, hexchars);
88                 
89                 pwd->hash[i / 2] = (hinybble << 4) | lonybble;
90         }
91         return pwd;
92 }
93
94 /*! Convert a 16-byte array into 32 hex characters. */
95         struct samr_Password *lm_hash_p = NULL;
96         struct samr_Password *nt_hash_p = NULL;
97
98 char *smbpasswd_sethexpwd(TALLOC_CTX *mem_ctx, struct samr_Password *pwd, uint16_t acb_info)
99 {
100         char *p;
101         if (pwd != NULL) {
102                 int i;
103                 p = talloc_array(mem_ctx, char, 33);
104                 if (!p) {
105                         return NULL;
106                 }
107
108                 for (i = 0; i < sizeof(pwd->hash); i++)
109                         slprintf(&p[i*2], 3, "%02X", pwd->hash[i]);
110         } else {
111                 if (acb_info & ACB_PWNOTREQ)
112                         p = talloc_strdup(mem_ctx, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
113                 else
114                         p = talloc_strdup(mem_ctx, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
115         }
116         return p;
117 }
118
119 /*! Decode the account control bits (ACB) info from a string. */
120
121 uint16_t smbpasswd_decode_acb_info(const char *p)
122 {
123         uint16_t acb_info = 0;
124         BOOL finished = False;
125
126         /*
127          * Check if the account type bits have been encoded after the
128          * NT password (in the form [NDHTUWSLXI]).
129          */
130
131         if (*p != '[') return 0;
132
133         for (p++; *p && !finished; p++)
134         {
135                 switch (*p) {
136                 case 'N': /* 'N'o password. */
137                         acb_info |= ACB_PWNOTREQ; 
138                         break;
139                 case 'D': /* 'D'isabled. */
140                         acb_info |= ACB_DISABLED; 
141                         break; 
142                 case 'H': /* 'H'omedir required. */
143                         acb_info |= ACB_HOMDIRREQ; 
144                         break;
145                 case 'T': /* 'T'emp account. */
146                         acb_info |= ACB_TEMPDUP; 
147                         break;
148                 case 'U': /* 'U'ser account (normal). */
149                         acb_info |= ACB_NORMAL;
150                         break;
151                 case 'M': /* 'M'NS logon user account. What is this ? */
152                         acb_info |= ACB_MNS; 
153                         break; 
154                 case 'W': /* 'W'orkstation account. */
155                         acb_info |= ACB_WSTRUST; 
156                         break; 
157                 case 'S': /* 'S'erver account. */ 
158                         acb_info |= ACB_SVRTRUST; 
159                         break; 
160                 case 'L': /* 'L'ocked account. */
161                         acb_info |= ACB_AUTOLOCK; 
162                         break; 
163                 case 'X': /* No 'X'piry on password */
164                         acb_info |= ACB_PWNOEXP; 
165                         break; 
166                 case 'I': /* 'I'nterdomain trust account. */
167                         acb_info |= ACB_DOMTRUST; 
168                         break; 
169
170                 case ' ': 
171                         break;
172                 case ':':
173                 case '\n':
174                 case '\0': 
175                 case ']':
176                 default:  
177                         finished = True;
178                         break;
179                 }
180         }
181
182         return acb_info;
183 }
184
185 /*! Encode account control bits (ACBs) into a string. */
186
187 char *smbpasswd_encode_acb_info(TALLOC_CTX *mem_ctx, uint16_t acb_info)
188 {
189         char *acct_str = talloc_array(mem_ctx, char, 35);
190         size_t i = 0;
191
192         acct_str[i++] = '[';
193
194         if (acb_info & ACB_PWNOTREQ ) acct_str[i++] = 'N';
195         if (acb_info & ACB_DISABLED ) acct_str[i++] = 'D';
196         if (acb_info & ACB_HOMDIRREQ) acct_str[i++] = 'H';
197         if (acb_info & ACB_TEMPDUP  ) acct_str[i++] = 'T'; 
198         if (acb_info & ACB_NORMAL   ) acct_str[i++] = 'U';
199         if (acb_info & ACB_MNS      ) acct_str[i++] = 'M';
200         if (acb_info & ACB_WSTRUST  ) acct_str[i++] = 'W';
201         if (acb_info & ACB_SVRTRUST ) acct_str[i++] = 'S';
202         if (acb_info & ACB_AUTOLOCK ) acct_str[i++] = 'L';
203         if (acb_info & ACB_PWNOEXP  ) acct_str[i++] = 'X';
204         if (acb_info & ACB_DOMTRUST ) acct_str[i++] = 'I';
205
206         acct_str[i++] = ']';
207         acct_str[i++] = '\0';
208
209         return acct_str;
210 }     
211
212 NTSTATUS samba3_read_smbpasswd(const char *filename, TALLOC_CTX *ctx, struct samba3_samaccount **accounts, uint32_t *count)
213 {
214         int numlines;
215         char **lines;
216         *count = 0;
217         *accounts = NULL;
218         int i;
219
220         lines = file_lines_load(filename, &numlines, ctx);
221
222         *accounts = talloc_array(ctx, struct samba3_samaccount, numlines);
223
224         for (i = 0; i < numlines; i++) {
225                 char *p = lines[i], *q;
226                 struct samba3_samaccount *acc = &((*accounts)[*count]);
227
228                 if (p[0] == '\0' || p[0] == '#')
229                         continue;
230
231                 ZERO_STRUCTP(acc);
232
233                 q = strchr(p, ':');
234                 if (!q) {
235                         DEBUG(0, ("%s:%d: expected ':'\n", filename, i));
236                         continue;
237                 }
238
239                 acc->username = talloc_strndup(ctx, p, PTR_DIFF(q, p));
240                 p = q+1;
241
242                 acc->uid = atoi(p);
243
244                 q = strchr(p, ':');
245                 if (!q) {
246                         DEBUG(0, ("%s:%d: expected ':'\n", filename, i));
247                         continue;
248                 }
249                 p = q+1;
250
251                 if (strlen(p) < 33) {
252                         DEBUG(0, ("%s:%d: expected 32 byte password blob\n", filename, i));
253                         continue;
254                 }
255
256                 if (!strncmp(p, "NO PASSWORD", strlen("NO PASSWORD"))) {
257                         acc->acct_ctrl |= ACB_PWNOTREQ;
258                 } else if (p[0] == '*' || p[0] == 'X') {
259                         /* No password set */
260                 } else {
261                         struct samr_Password *pw = smbpasswd_gethexpwd(*accounts, p);
262                         
263                         if (!pw) {
264                                 DEBUG(0, ("%s:%d: Malformed LM pw entry\n", filename, i));
265                                 continue;
266                         }
267
268                         memcpy(acc->lm_pw.hash, pw, sizeof(*pw));
269                 }
270
271                 if (p[32] != ':') {
272                         DEBUG(0, ("%s:%d: expected ':' after 32 byte password blob\n", filename, i));
273                         continue;
274                 }
275
276                 p += 33;
277                 
278                 if (p[0] == '*' || p[0] == 'X') {
279                         /* No password set */
280                 } else {
281                         struct samr_Password *pw = smbpasswd_gethexpwd(*accounts, p);
282                         
283                         if (!pw) {
284                                 DEBUG(0, ("%s:%d: Malformed LM pw entry\n", filename, i));
285                                 continue;
286                         }
287
288                         memcpy(acc->nt_pw.hash, pw, sizeof(*pw));
289                 }
290                 
291                 if (p[32] != ':') {
292                         DEBUG(0, ("%s:%d: expected ':' after 32 byte password blob\n", filename, i));
293                         continue;
294                 }
295
296                 p += 33;
297
298                 if (p[0] == '[') {
299                         q = strchr(p, ']');
300                         if (!q) {
301                                 DEBUG(0, ("%s:%d: expected ']'\n", filename, i));
302                                 continue;
303                         }
304                         
305                         acc->acct_ctrl |= smbpasswd_decode_acb_info(p);
306
307                         p = q+1;
308                         if (p[0] == ':' && strncmp(p, "LCT-", 4) == 0) {
309                                 int j;
310                                 p += 4;
311
312                                 for(j = 0; j < 8; j++) {
313                                         if(p[j] == '\0' || !isxdigit(p[j])) {
314                                                 break;
315                                         }
316                                 }
317                                 if(i == 8) {
318                                         acc->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
319                                 }
320                         }
321                 } else {
322                         /* 'Old' style file. Fake up based on user name. */
323                         /*
324                          * Currently trust accounts are kept in the same
325                          * password file as 'normal accounts'. If this changes
326                          * we will have to fix this code. JRA.
327                          */
328                         if(acc->username[strlen(acc->username) - 1] == '$') {
329                                 acc->acct_ctrl &= ~ACB_NORMAL;
330                                 acc->acct_ctrl |= ACB_WSTRUST;
331                         }
332                 }
333
334                 (*count)++;
335         }
336
337         talloc_free(lines);
338
339         return NT_STATUS_OK;
340 }