6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2001 The FreeRADIUS server project
24 * smbpass.c contains a set of functions required to handle passwd
25 * files in SAMBA format. Some pieces of code were adopted from SAMBA
28 * ZARAZA 3APA3A@security.nnov.ru
32 #include <sys/types.h>
40 void pdb_init_smb(struct smb_passwd *user)
42 if (user == NULL) return;
43 memset((char *)user, '\0', sizeof(*user));
44 user->pass_last_set_time = (time_t)-1;
45 user->smb_passwd = NULL;
46 user->smb_nt_passwd = NULL;
47 memset(user->smb_name_value,0,256);
48 memset(user->smb_passwd_value,0,16);
49 memset(user->smb_nt_passwd_value,0,16);
54 uint16 pdb_decode_acct_ctrl(const char *p)
60 * Check if the account type bits have been encoded after the
61 * NT password (in the form [NDHTUWSLXI]).
64 if (*p != '[') return 0;
66 for (p++; *p && !finished; p++)
70 case 'N': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
71 case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
72 case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
73 case 'T': { acct_ctrl |= ACB_TEMPDUP ; break; /* 'T'emp account. */ }
74 case 'U': { acct_ctrl |= ACB_NORMAL ; break; /* 'U'ser account (normal). */ }
75 case 'M': { acct_ctrl |= ACB_MNS ; break; /* 'M'NS logon user account. What is this ? */ }
76 case 'W': { acct_ctrl |= ACB_WSTRUST ; break; /* 'W'orkstation account. */ }
77 case 'S': { acct_ctrl |= ACB_SVRTRUST ; break; /* 'S'erver account. */ }
78 case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ }
79 case 'X': { acct_ctrl |= ACB_PWNOEXP ; break; /* No 'X'piry on password */ }
80 case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
86 default: { finished = 1; }
94 static char letters[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
95 'A', 'B', 'C', 'D', 'E', 'F'};
99 * hex2bin converts hexadecimal strings into binary
101 int hex2bin (const char *szHex, unsigned char* szBin, int len)
106 for (i = 0; i < len; i++) {
107 if( !(c1 = memchr(letters, toupper(szHex[i << 1]), 16)) ||
108 !(c2 = memchr(letters, toupper(szHex[(i << 1) + 1]), 16)))
110 szBin[i] = ((c1-letters)<<4) + (c2-letters);
116 * bin2hex creates hexadecimal presentation
119 void bin2hex (const unsigned char *szBin, char *szHex, int len)
122 for (i = 0; i < len; i++) {
123 szHex[i<<1] = letters[szBin[i] >> 4];
124 szHex[(i<<1) + 1] = letters[szBin[i] & 0x0F];
130 struct smb_passwd *getsmbfilepwent(struct smb_passwd *pw_buf, FILE *fp)
132 /* Static buffers we will return. */
141 if(fp == NULL || pw_buf == NULL) {
145 pdb_init_smb(pw_buf);
147 pw_buf->acct_ctrl = ACB_NORMAL;
150 * Scan the file, a line at a time and check if the name matches.
154 fgets(linebuf, 256, fp);
160 * Check if the string is terminated with a newline - if not
161 * then we must keep reading and discard until we get one.
163 if ((linebuf_len = strlen(linebuf)) == 0)
166 if (linebuf[linebuf_len - 1] != '\n') {
168 while (!ferror(fp) && !feof(fp)) {
174 linebuf[linebuf_len - 1] = '\0';
176 if ((linebuf[0] == 0) && feof(fp)) {
180 * The line we have should be of the form :-
182 * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
187 * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
189 * if Windows NT compatible passwords are also present.
190 * [Account type] is an ascii encoding of the type of account.
191 * LCT-(8 hex digits) is the time_t value of the last change time.
194 if (linebuf[0] == '#' || linebuf[0] == '\0') {
197 p = strchr(linebuf, ':');
202 * As 256 is shorter than a pstring we don't need to check
203 * length here - if this ever changes....
205 strncpy(user_name, linebuf, p - linebuf);
206 user_name[p - linebuf] = '\0';
210 p++; /* Go past ':' */
222 while (*p && isdigit(*p))
229 setsmbname(pw_buf,user_name);
230 pw_buf->smb_userid = uidval;
233 * Now get the password value - this should be 32 hex digits
234 * which are the ascii representations of a 16 byte string.
235 * Get two at a time and put them into the password.
241 if (*p == '*' || *p == 'X') {
242 /* Password deliberately invalid - end here. */
243 pw_buf->smb_nt_passwd = NULL;
244 pw_buf->smb_passwd = NULL;
245 pw_buf->acct_ctrl |= ACB_DISABLED;
249 if (linebuf_len < ((p - linebuf) + 33)) {
257 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
258 pw_buf->smb_passwd = NULL;
259 pw_buf->acct_ctrl |= ACB_PWNOTREQ;
261 if (hex2bin((char *)p, pw_buf->smb_passwd_value, 16) != 16) {
264 pw_buf->smb_passwd = pw_buf->smb_passwd_value;
268 * Now check if the NT compatible password is
271 pw_buf->smb_nt_passwd = NULL;
273 p += 33; /* Move to the first character of the line after
274 the lanman password. */
275 if ((linebuf_len >= ((p - linebuf) + 33)) && (p[32] == ':')) {
276 if (*p != '*' && *p != 'X') {
277 if(hex2bin((char *)p, pw_buf->smb_nt_passwd_value, 16)==16)
278 pw_buf->smb_nt_passwd = pw_buf->smb_nt_passwd_value;
280 p += 33; /* Move to the first character of the line after
286 unsigned char *end_p = (unsigned char *)strchr((char *)p, ']');
287 pw_buf->acct_ctrl = pdb_decode_acct_ctrl((char*)p);
288 /* Must have some account type set. */
289 if(pw_buf->acct_ctrl == 0)
290 pw_buf->acct_ctrl = ACB_NORMAL;
292 /* Now try and get the last change time. */
297 if(*p && (strncasecmp(p, "LCT-", 4)==0)) {
300 for(i = 0; i < 8; i++) {
301 if(p[i] == '\0' || !isxdigit(p[i]))
306 * p points at 8 characters of hex digits -
307 * read into a time_t as the seconds since
308 * 1970 that the password was last changed.
310 pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
315 /* 'Old' style file. Fake up based on user name. */
317 * Currently trust accounts are kept in the same
318 * password file as 'normal accounts'. If this changes
319 * we will have to fix this code. JRA.
321 if(pw_buf->smb_name[strlen(pw_buf->smb_name) - 1] == '$') {
322 pw_buf->acct_ctrl &= ~ACB_NORMAL;
323 pw_buf->acct_ctrl |= ACB_WSTRUST;
332 struct smb_passwd *getsmbfilepwname(struct smb_passwd *pw_buf,const char *fname, const char *name)
336 if(!pw_buf)return NULL;
337 file = fopen(fname, "ro");
338 if (file == NULL) return NULL;
339 while ( getsmbfilepwent(pw_buf, file) && strcmp(pw_buf->smb_name, name))
345 void setsmbname(struct smb_passwd *pw_buf,const char *name)
347 strncpy((char*)pw_buf->smb_name_value,name,255);
348 pw_buf->smb_name_value[255] = '\0';
349 pw_buf->smb_name = pw_buf->smb_name_value;