r20134: The IBM Checker correctly notes that *p cannot be \0 and still satisfy
[Samba.git] / source / lib / samba3 / smbpasswd.c
blob14715a89f739cad303f9238c1e9070b3d9aa3605
1 /*
2 Unix SMB/CIFS implementation.
3 smbpasswd file format routines
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
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.
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.
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.
27 /*! \file lib/smbpasswd.c
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:
33 <username>:<uid>:<lanman hash>:<nt hash>:<acb info>:<last change time>
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.
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
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
56 #include "includes.h"
57 #include "system/locale.h"
58 #include "lib/samba3/samba3.h"
60 /*! Convert 32 hex characters into a 16 byte array. */
62 struct samr_Password *smbpasswd_gethexpwd(TALLOC_CTX *mem_ctx, const char *p)
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);
70 if (!p) return NULL;
72 for (i = 0; i < (sizeof(pwd->hash) * 2); i += 2)
74 hinybble = toupper(p[i]);
75 lonybble = toupper(p[i + 1]);
77 p1 = strchr_m(hexchars, hinybble);
78 p2 = strchr_m(hexchars, lonybble);
80 if (!p1 || !p2) {
81 return NULL;
84 hinybble = PTR_DIFF(p1, hexchars);
85 lonybble = PTR_DIFF(p2, hexchars);
87 pwd->hash[i / 2] = (hinybble << 4) | lonybble;
89 return pwd;
92 /*! Convert a 16-byte array into 32 hex characters. */
93 struct samr_Password *lm_hash_p = NULL;
94 struct samr_Password *nt_hash_p = NULL;
96 char *smbpasswd_sethexpwd(TALLOC_CTX *mem_ctx, struct samr_Password *pwd, uint16_t acb_info)
98 char *p;
99 if (pwd != NULL) {
100 int i;
101 p = talloc_array(mem_ctx, char, 33);
102 if (!p) {
103 return NULL;
106 for (i = 0; i < sizeof(pwd->hash); i++)
107 slprintf(&p[i*2], 3, "%02X", pwd->hash[i]);
108 } else {
109 if (acb_info & ACB_PWNOTREQ)
110 p = talloc_strdup(mem_ctx, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
111 else
112 p = talloc_strdup(mem_ctx, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
114 return p;
117 /*! Decode the account control bits (ACB) info from a string. */
119 uint16_t smbpasswd_decode_acb_info(const char *p)
121 uint16_t acb_info = 0;
122 BOOL finished = False;
125 * Check if the account type bits have been encoded after the
126 * NT password (in the form [NDHTUWSLXI]).
129 if (*p != '[') return 0;
131 for (p++; *p && !finished; p++)
133 switch (*p) {
134 case 'N': /* 'N'o password. */
135 acb_info |= ACB_PWNOTREQ;
136 break;
137 case 'D': /* 'D'isabled. */
138 acb_info |= ACB_DISABLED;
139 break;
140 case 'H': /* 'H'omedir required. */
141 acb_info |= ACB_HOMDIRREQ;
142 break;
143 case 'T': /* 'T'emp account. */
144 acb_info |= ACB_TEMPDUP;
145 break;
146 case 'U': /* 'U'ser account (normal). */
147 acb_info |= ACB_NORMAL;
148 break;
149 case 'M': /* 'M'NS logon user account. What is this ? */
150 acb_info |= ACB_MNS;
151 break;
152 case 'W': /* 'W'orkstation account. */
153 acb_info |= ACB_WSTRUST;
154 break;
155 case 'S': /* 'S'erver account. */
156 acb_info |= ACB_SVRTRUST;
157 break;
158 case 'L': /* 'L'ocked account. */
159 acb_info |= ACB_AUTOLOCK;
160 break;
161 case 'X': /* No 'X'piry on password */
162 acb_info |= ACB_PWNOEXP;
163 break;
164 case 'I': /* 'I'nterdomain trust account. */
165 acb_info |= ACB_DOMTRUST;
166 break;
168 case ' ':
169 break;
170 case ':':
171 case '\n':
172 case ']':
173 default:
174 finished = True;
175 break;
179 return acb_info;
182 /*! Encode account control bits (ACBs) into a string. */
184 char *smbpasswd_encode_acb_info(TALLOC_CTX *mem_ctx, uint16_t acb_info)
186 char *acct_str = talloc_array(mem_ctx, char, 35);
187 size_t i = 0;
189 acct_str[i++] = '[';
191 if (acb_info & ACB_PWNOTREQ ) acct_str[i++] = 'N';
192 if (acb_info & ACB_DISABLED ) acct_str[i++] = 'D';
193 if (acb_info & ACB_HOMDIRREQ) acct_str[i++] = 'H';
194 if (acb_info & ACB_TEMPDUP ) acct_str[i++] = 'T';
195 if (acb_info & ACB_NORMAL ) acct_str[i++] = 'U';
196 if (acb_info & ACB_MNS ) acct_str[i++] = 'M';
197 if (acb_info & ACB_WSTRUST ) acct_str[i++] = 'W';
198 if (acb_info & ACB_SVRTRUST ) acct_str[i++] = 'S';
199 if (acb_info & ACB_AUTOLOCK ) acct_str[i++] = 'L';
200 if (acb_info & ACB_PWNOEXP ) acct_str[i++] = 'X';
201 if (acb_info & ACB_DOMTRUST ) acct_str[i++] = 'I';
203 acct_str[i++] = ']';
204 acct_str[i++] = '\0';
206 return acct_str;
209 NTSTATUS samba3_read_smbpasswd(const char *filename, TALLOC_CTX *ctx, struct samba3_samaccount **accounts, uint32_t *count)
211 int numlines;
212 char **lines;
213 int i;
215 *count = 0;
216 *accounts = NULL;
218 lines = file_lines_load(filename, &numlines, ctx);
220 if (lines == NULL) {
221 DEBUG(0, ("Unable to load lines from %s\n", filename));
222 return NT_STATUS_UNSUCCESSFUL;
225 *accounts = talloc_array(ctx, struct samba3_samaccount, numlines);
227 for (i = 0; i < numlines; i++) {
228 char *p = lines[i], *q;
229 uid_t uid;
230 struct samba3_samaccount *acc = &((*accounts)[*count]);
232 if (p[0] == '\0' || p[0] == '#')
233 continue;
235 ZERO_STRUCTP(acc);
237 q = strchr(p, ':');
238 if (!q) {
239 DEBUG(0, ("%s:%d: expected ':'\n", filename, i));
240 continue;
243 acc->username = talloc_strndup(ctx, p, PTR_DIFF(q, p));
244 p = q+1;
246 uid = atoi(p);
248 /* uid is ignored here.. */
250 q = strchr(p, ':');
251 if (!q) {
252 DEBUG(0, ("%s:%d: expected ':'\n", filename, i));
253 continue;
255 p = q+1;
257 if (strlen(p) < 33) {
258 DEBUG(0, ("%s:%d: expected 32 byte password blob\n", filename, i));
259 continue;
262 if (!strncmp(p, "NO PASSWORD", strlen("NO PASSWORD"))) {
263 acc->acct_ctrl |= ACB_PWNOTREQ;
264 } else if (p[0] == '*' || p[0] == 'X') {
265 /* No password set */
266 } else {
267 struct samr_Password *pw = smbpasswd_gethexpwd(*accounts, p);
269 if (!pw) {
270 DEBUG(0, ("%s:%d: Malformed LM pw entry\n", filename, i));
271 continue;
274 memcpy(acc->lm_pw.hash, pw, sizeof(*pw));
277 if (p[32] != ':') {
278 DEBUG(0, ("%s:%d: expected ':' after 32 byte password blob\n", filename, i));
279 continue;
282 p += 33;
284 if (p[0] == '*' || p[0] == 'X') {
285 /* No password set */
286 } else {
287 struct samr_Password *pw = smbpasswd_gethexpwd(*accounts, p);
289 if (!pw) {
290 DEBUG(0, ("%s:%d: Malformed LM pw entry\n", filename, i));
291 continue;
294 memcpy(acc->nt_pw.hash, pw, sizeof(*pw));
297 if (p[32] != ':') {
298 DEBUG(0, ("%s:%d: expected ':' after 32 byte password blob\n", filename, i));
299 continue;
302 p += 33;
304 if (p[0] == '[') {
305 q = strchr(p, ']');
306 if (!q) {
307 DEBUG(0, ("%s:%d: expected ']'\n", filename, i));
308 continue;
311 acc->acct_ctrl |= smbpasswd_decode_acb_info(p);
313 p = q+1;
314 if (p[0] == ':' && strncmp(p, "LCT-", 4) == 0) {
315 int j;
316 p += 4;
318 for(j = 0; j < 8; j++) {
319 if(p[j] == '\0' || !isxdigit(p[j])) {
320 break;
323 if(i == 8) {
324 acc->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
327 } else {
328 /* 'Old' style file. Fake up based on user name. */
330 * Currently trust accounts are kept in the same
331 * password file as 'normal accounts'. If this changes
332 * we will have to fix this code. JRA.
334 if(acc->username[strlen(acc->username) - 1] == '$') {
335 acc->acct_ctrl &= ~ACB_NORMAL;
336 acc->acct_ctrl |= ACB_WSTRUST;
340 (*count)++;
343 talloc_free(lines);
345 return NT_STATUS_OK;