r12608: Remove some unused #include lines.
[abartlet/samba.git/.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 "lib/samba3/samba3.h"
58 #include "system/iconv.h"
59
60 /*! Convert 32 hex characters into a 16 byte array. */
61
62 struct samr_Password *smbpasswd_gethexpwd(TALLOC_CTX *mem_ctx, const char *p)
63 {
64         int i;
65         unsigned char   lonybble, hinybble;
66         const char     *hexchars = "0123456789ABCDEF";
67         const char     *p1, *p2;
68         struct samr_Password *pwd = talloc(mem_ctx, struct samr_Password);
69
70         if (!p) return NULL;
71         
72         for (i = 0; i < (sizeof(pwd->hash) * 2); i += 2)
73         {
74                 hinybble = toupper(p[i]);
75                 lonybble = toupper(p[i + 1]);
76                 
77                 p1 = strchr_m(hexchars, hinybble);
78                 p2 = strchr_m(hexchars, lonybble);
79                 
80                 if (!p1 || !p2)
81                 {
82                         return (False);
83                 }
84                 
85                 hinybble = PTR_DIFF(p1, hexchars);
86                 lonybble = PTR_DIFF(p2, hexchars);
87                 
88                 pwd->hash[i / 2] = (hinybble << 4) | lonybble;
89         }
90         return pwd;
91 }
92
93 /*! Convert a 16-byte array into 32 hex characters. */
94         struct samr_Password *lm_hash_p = NULL;
95         struct samr_Password *nt_hash_p = NULL;
96
97 char *smbpasswd_sethexpwd(TALLOC_CTX *mem_ctx, struct samr_Password *pwd, uint16_t acb_info)
98 {
99         char *p;
100         if (pwd != NULL) {
101                 int i;
102                 p = talloc_array(mem_ctx, char, 33);
103                 if (!p) {
104                         return NULL;
105                 }
106
107                 for (i = 0; i < sizeof(pwd->hash); i++)
108                         slprintf(&p[i*2], 3, "%02X", pwd->hash[i]);
109         } else {
110                 if (acb_info & ACB_PWNOTREQ)
111                         p = talloc_strdup(mem_ctx, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
112                 else
113                         p = talloc_strdup(mem_ctx, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
114         }
115         return p;
116 }
117
118 /*! Decode the account control bits (ACB) info from a string. */
119
120 uint16_t smbpasswd_decode_acb_info(const char *p)
121 {
122         uint16_t acb_info = 0;
123         BOOL finished = False;
124
125         /*
126          * Check if the account type bits have been encoded after the
127          * NT password (in the form [NDHTUWSLXI]).
128          */
129
130         if (*p != '[') return 0;
131
132         for (p++; *p && !finished; p++)
133         {
134                 switch (*p) {
135                 case 'N': /* 'N'o password. */
136                         acb_info |= ACB_PWNOTREQ; 
137                         break;
138                 case 'D': /* 'D'isabled. */
139                         acb_info |= ACB_DISABLED; 
140                         break; 
141                 case 'H': /* 'H'omedir required. */
142                         acb_info |= ACB_HOMDIRREQ; 
143                         break;
144                 case 'T': /* 'T'emp account. */
145                         acb_info |= ACB_TEMPDUP; 
146                         break;
147                 case 'U': /* 'U'ser account (normal). */
148                         acb_info |= ACB_NORMAL;
149                         break;
150                 case 'M': /* 'M'NS logon user account. What is this ? */
151                         acb_info |= ACB_MNS; 
152                         break; 
153                 case 'W': /* 'W'orkstation account. */
154                         acb_info |= ACB_WSTRUST; 
155                         break; 
156                 case 'S': /* 'S'erver account. */ 
157                         acb_info |= ACB_SVRTRUST; 
158                         break; 
159                 case 'L': /* 'L'ocked account. */
160                         acb_info |= ACB_AUTOLOCK; 
161                         break; 
162                 case 'X': /* No 'X'piry on password */
163                         acb_info |= ACB_PWNOEXP; 
164                         break; 
165                 case 'I': /* 'I'nterdomain trust account. */
166                         acb_info |= ACB_DOMTRUST; 
167                         break; 
168
169                 case ' ': 
170                         break;
171                 case ':':
172                 case '\n':
173                 case '\0': 
174                 case ']':
175                 default:  
176                         finished = True;
177                         break;
178                 }
179         }
180
181         return acb_info;
182 }
183
184 /*! Encode account control bits (ACBs) into a string. */
185
186 char *smbpasswd_encode_acb_info(TALLOC_CTX *mem_ctx, uint16_t acb_info)
187 {
188         char *acct_str = talloc_array(mem_ctx, char, 35);
189         size_t i = 0;
190
191         acct_str[i++] = '[';
192
193         if (acb_info & ACB_PWNOTREQ ) acct_str[i++] = 'N';
194         if (acb_info & ACB_DISABLED ) acct_str[i++] = 'D';
195         if (acb_info & ACB_HOMDIRREQ) acct_str[i++] = 'H';
196         if (acb_info & ACB_TEMPDUP  ) acct_str[i++] = 'T'; 
197         if (acb_info & ACB_NORMAL   ) acct_str[i++] = 'U';
198         if (acb_info & ACB_MNS      ) acct_str[i++] = 'M';
199         if (acb_info & ACB_WSTRUST  ) acct_str[i++] = 'W';
200         if (acb_info & ACB_SVRTRUST ) acct_str[i++] = 'S';
201         if (acb_info & ACB_AUTOLOCK ) acct_str[i++] = 'L';
202         if (acb_info & ACB_PWNOEXP  ) acct_str[i++] = 'X';
203         if (acb_info & ACB_DOMTRUST ) acct_str[i++] = 'I';
204
205         acct_str[i++] = ']';
206         acct_str[i++] = '\0';
207
208         return acct_str;
209 }     
210
211 NTSTATUS samba3_read_smbpasswd(const char *filename, TALLOC_CTX *ctx, struct samba3_samaccount **accounts, uint32_t *count)
212 {
213         int numlines;
214         char **lines;
215         int i;
216
217         *count = 0;
218         *accounts = NULL;
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                 uid_t uid;
232                 struct samba3_samaccount *acc = &((*accounts)[*count]);
233
234                 if (p[0] == '\0' || p[0] == '#')
235                         continue;
236
237                 ZERO_STRUCTP(acc);
238
239                 q = strchr(p, ':');
240                 if (!q) {
241                         DEBUG(0, ("%s:%d: expected ':'\n", filename, i));
242                         continue;
243                 }
244
245                 acc->username = talloc_strndup(ctx, p, PTR_DIFF(q, p));
246                 p = q+1;
247
248                 uid = atoi(p);
249                 
250                 /* uid is ignored here.. */
251
252                 q = strchr(p, ':');
253                 if (!q) {
254                         DEBUG(0, ("%s:%d: expected ':'\n", filename, i));
255                         continue;
256                 }
257                 p = q+1;
258
259                 if (strlen(p) < 33) {
260                         DEBUG(0, ("%s:%d: expected 32 byte password blob\n", filename, i));
261                         continue;
262                 }
263
264                 if (!strncmp(p, "NO PASSWORD", strlen("NO PASSWORD"))) {
265                         acc->acct_ctrl |= ACB_PWNOTREQ;
266                 } else if (p[0] == '*' || p[0] == 'X') {
267                         /* No password set */
268                 } else {
269                         struct samr_Password *pw = smbpasswd_gethexpwd(*accounts, p);
270                         
271                         if (!pw) {
272                                 DEBUG(0, ("%s:%d: Malformed LM pw entry\n", filename, i));
273                                 continue;
274                         }
275
276                         memcpy(acc->lm_pw.hash, pw, sizeof(*pw));
277                 }
278
279                 if (p[32] != ':') {
280                         DEBUG(0, ("%s:%d: expected ':' after 32 byte password blob\n", filename, i));
281                         continue;
282                 }
283
284                 p += 33;
285                 
286                 if (p[0] == '*' || p[0] == 'X') {
287                         /* No password set */
288                 } else {
289                         struct samr_Password *pw = smbpasswd_gethexpwd(*accounts, p);
290                         
291                         if (!pw) {
292                                 DEBUG(0, ("%s:%d: Malformed LM pw entry\n", filename, i));
293                                 continue;
294                         }
295
296                         memcpy(acc->nt_pw.hash, pw, sizeof(*pw));
297                 }
298                 
299                 if (p[32] != ':') {
300                         DEBUG(0, ("%s:%d: expected ':' after 32 byte password blob\n", filename, i));
301                         continue;
302                 }
303
304                 p += 33;
305
306                 if (p[0] == '[') {
307                         q = strchr(p, ']');
308                         if (!q) {
309                                 DEBUG(0, ("%s:%d: expected ']'\n", filename, i));
310                                 continue;
311                         }
312                         
313                         acc->acct_ctrl |= smbpasswd_decode_acb_info(p);
314
315                         p = q+1;
316                         if (p[0] == ':' && strncmp(p, "LCT-", 4) == 0) {
317                                 int j;
318                                 p += 4;
319
320                                 for(j = 0; j < 8; j++) {
321                                         if(p[j] == '\0' || !isxdigit(p[j])) {
322                                                 break;
323                                         }
324                                 }
325                                 if(i == 8) {
326                                         acc->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
327                                 }
328                         }
329                 } else {
330                         /* 'Old' style file. Fake up based on user name. */
331                         /*
332                          * Currently trust accounts are kept in the same
333                          * password file as 'normal accounts'. If this changes
334                          * we will have to fix this code. JRA.
335                          */
336                         if(acc->username[strlen(acc->username) - 1] == '$') {
337                                 acc->acct_ctrl &= ~ACB_NORMAL;
338                                 acc->acct_ctrl |= ACB_WSTRUST;
339                         }
340                 }
341
342                 (*count)++;
343         }
344
345         talloc_free(lines);
346
347         return NT_STATUS_OK;
348 }