r9808: Improve code that selects what "passdb backend" to import from.
[ira/wip.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         if (lines == NULL) {
223                 DEBUG(0, ("Unable to load lines from %s\n", filename));
224                 return NT_STATUS_UNSUCCESSFUL;
225         }
226
227         *accounts = talloc_array(ctx, struct samba3_samaccount, numlines);
228
229         for (i = 0; i < numlines; i++) {
230                 char *p = lines[i], *q;
231                 struct samba3_samaccount *acc = &((*accounts)[*count]);
232
233                 if (p[0] == '\0' || p[0] == '#')
234                         continue;
235
236                 ZERO_STRUCTP(acc);
237
238                 q = strchr(p, ':');
239                 if (!q) {
240                         DEBUG(0, ("%s:%d: expected ':'\n", filename, i));
241                         continue;
242                 }
243
244                 acc->username = talloc_strndup(ctx, p, PTR_DIFF(q, p));
245                 p = q+1;
246
247                 acc->uid = atoi(p);
248
249                 q = strchr(p, ':');
250                 if (!q) {
251                         DEBUG(0, ("%s:%d: expected ':'\n", filename, i));
252                         continue;
253                 }
254                 p = q+1;
255
256                 if (strlen(p) < 33) {
257                         DEBUG(0, ("%s:%d: expected 32 byte password blob\n", filename, i));
258                         continue;
259                 }
260
261                 if (!strncmp(p, "NO PASSWORD", strlen("NO PASSWORD"))) {
262                         acc->acct_ctrl |= ACB_PWNOTREQ;
263                 } else if (p[0] == '*' || p[0] == 'X') {
264                         /* No password set */
265                 } else {
266                         struct samr_Password *pw = smbpasswd_gethexpwd(*accounts, p);
267                         
268                         if (!pw) {
269                                 DEBUG(0, ("%s:%d: Malformed LM pw entry\n", filename, i));
270                                 continue;
271                         }
272
273                         memcpy(acc->lm_pw.hash, pw, sizeof(*pw));
274                 }
275
276                 if (p[32] != ':') {
277                         DEBUG(0, ("%s:%d: expected ':' after 32 byte password blob\n", filename, i));
278                         continue;
279                 }
280
281                 p += 33;
282                 
283                 if (p[0] == '*' || p[0] == 'X') {
284                         /* No password set */
285                 } else {
286                         struct samr_Password *pw = smbpasswd_gethexpwd(*accounts, p);
287                         
288                         if (!pw) {
289                                 DEBUG(0, ("%s:%d: Malformed LM pw entry\n", filename, i));
290                                 continue;
291                         }
292
293                         memcpy(acc->nt_pw.hash, pw, sizeof(*pw));
294                 }
295                 
296                 if (p[32] != ':') {
297                         DEBUG(0, ("%s:%d: expected ':' after 32 byte password blob\n", filename, i));
298                         continue;
299                 }
300
301                 p += 33;
302
303                 if (p[0] == '[') {
304                         q = strchr(p, ']');
305                         if (!q) {
306                                 DEBUG(0, ("%s:%d: expected ']'\n", filename, i));
307                                 continue;
308                         }
309                         
310                         acc->acct_ctrl |= smbpasswd_decode_acb_info(p);
311
312                         p = q+1;
313                         if (p[0] == ':' && strncmp(p, "LCT-", 4) == 0) {
314                                 int j;
315                                 p += 4;
316
317                                 for(j = 0; j < 8; j++) {
318                                         if(p[j] == '\0' || !isxdigit(p[j])) {
319                                                 break;
320                                         }
321                                 }
322                                 if(i == 8) {
323                                         acc->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
324                                 }
325                         }
326                 } else {
327                         /* 'Old' style file. Fake up based on user name. */
328                         /*
329                          * Currently trust accounts are kept in the same
330                          * password file as 'normal accounts'. If this changes
331                          * we will have to fix this code. JRA.
332                          */
333                         if(acc->username[strlen(acc->username) - 1] == '$') {
334                                 acc->acct_ctrl &= ~ACB_NORMAL;
335                                 acc->acct_ctrl |= ACB_WSTRUST;
336                         }
337                 }
338
339                 (*count)++;
340         }
341
342         talloc_free(lines);
343
344         return NT_STATUS_OK;
345 }