0e2d7005b2c7b98b0540e1c27f2c6023f50f096c
[freeradius.git] / src / modules / rlm_mschap / smbpass.c
1 /*
2  * smbpass.c    
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2001  The FreeRADIUS server project
21  */
22  
23 /*
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
26  *   project.
27  *
28  *   ZARAZA     3APA3A@security.nnov.ru
29  */
30
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <string.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <stdlib.h>
37 #include <ctype.h>
38 #include "smbpass.h"
39
40 void pdb_init_smb(struct smb_passwd *user)
41 {
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);
50 }
51
52
53
54 uint16 pdb_decode_acct_ctrl(const char *p)
55 {
56         uint16 acct_ctrl = 0;
57         int finished = 0;
58
59         /*
60          * Check if the account type bits have been encoded after the
61          * NT password (in the form [NDHTUWSLXI]).
62          */
63
64         if (*p != '[') return 0;
65
66         for (p++; *p && !finished; p++)
67         {
68                 switch (*p)
69                 {
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. */ }
81                         case ' ': { break; }
82                         case ':':
83                         case '\n':
84                         case '\0': 
85                         case ']':
86                         default:  { finished = 1; }
87                 }
88         }
89
90         return acct_ctrl;
91 }
92
93
94 static char letters[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
95                         'A', 'B', 'C', 'D', 'E', 'F'};
96
97
98 /*
99  *      hex2bin converts hexadecimal strings into binary
100  */
101 int hex2bin (const char *szHex, unsigned char* szBin, int len)
102 {
103         char * c1, * c2;
104         int i;
105    
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)))
109                      break;
110                  szBin[i] = ((c1-letters)<<4) + (c2-letters);
111         }
112         return i;
113 }
114
115 /*
116  *      bin2hex creates hexadecimal presentation
117  *      of binary data
118  */ 
119 void bin2hex (const unsigned char *szBin, char *szHex, int len)
120 {
121         int i;
122         for (i = 0; i < len; i++) {
123                 szHex[i<<1] = letters[szBin[i] >> 4];
124                 szHex[(i<<1) + 1] = letters[szBin[i] & 0x0F];
125         }
126 }
127
128
129
130 struct smb_passwd *getsmbfilepwent(struct smb_passwd *pw_buf, FILE *fp)
131 {
132         /* Static buffers we will return. */
133         char user_name[256];
134         char            linebuf[256];
135         unsigned char   c;
136         char  *p;
137         long            uidval;
138         size_t            linebuf_len;
139
140
141         if(fp == NULL || pw_buf == NULL) {
142                 return NULL;
143         }
144
145         pdb_init_smb(pw_buf);
146
147         pw_buf->acct_ctrl = ACB_NORMAL;  
148
149         /*
150          * Scan the file, a line at a time and check if the name matches.
151          */
152         while (!feof(fp)) {
153                 linebuf[0] = '\0';
154                 fgets(linebuf, 256, fp);
155                 if (ferror(fp)) {
156                   return NULL;
157                 }
158
159                 /*
160                  * Check if the string is terminated with a newline - if not
161                  * then we must keep reading and discard until we get one.
162                  */
163                 if ((linebuf_len = strlen(linebuf)) == 0)
164                         continue;
165
166                 if (linebuf[linebuf_len - 1] != '\n') {
167                   c = '\0';
168                   while (!ferror(fp) && !feof(fp)) {
169                     c = fgetc(fp);
170                     if (c == '\n')
171                       break;
172                   }
173                 } else
174                   linebuf[linebuf_len - 1] = '\0';
175
176                 if ((linebuf[0] == 0) && feof(fp)) {
177                   break;
178                 }
179                 /*
180                  * The line we have should be of the form :-
181                  * 
182                  * username:uid:32hex bytes:[Account type]:LCT-12345678....other flags presently
183                  * ignored....
184                  * 
185                  * or,
186                  *
187                  * username:uid:32hex bytes:32hex bytes:[Account type]:LCT-12345678....ignored....
188                  *
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.
192                  */
193
194                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
195                   continue;
196                 }
197                 p = strchr(linebuf, ':');
198                 if (p == NULL) {
199                   continue;
200                 }
201                 /*
202                  * As 256 is shorter than a pstring we don't need to check
203                  * length here - if this ever changes....
204                  */
205                 strncpy(user_name, linebuf, p - linebuf);
206                 user_name[p - linebuf] = '\0';
207
208                 /* Get smb uid. */
209
210                 p++;            /* Go past ':' */
211
212                 if(*p == '-') {
213                   continue;
214                 }
215
216                 if (!isdigit(*p)) {
217                   continue;
218                 }
219
220                 uidval = atoi(p);
221
222                 while (*p && isdigit(*p))
223                   p++;
224
225                 if (*p != ':') {
226                   continue;
227                 }
228
229                 setsmbname(pw_buf,user_name);
230                 pw_buf->smb_userid = uidval;
231
232                 /*
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.
236                  */
237
238                 /* Skip the ':' */
239                 p++;
240
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;
246                   return pw_buf;
247                 }
248
249                 if (linebuf_len < ((p - linebuf) + 33)) {
250                   continue;
251                 }
252
253                 if (p[32] != ':') {
254                   continue;
255                 }
256
257                 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
258                   pw_buf->smb_passwd = NULL;
259                   pw_buf->acct_ctrl |= ACB_PWNOTREQ;
260                 } else {
261                   if (hex2bin((char *)p, pw_buf->smb_passwd_value, 16) != 16) {
262                     continue;
263                   }
264                   pw_buf->smb_passwd = pw_buf->smb_passwd_value;
265                 }
266
267                 /* 
268                  * Now check if the NT compatible password is
269                  * available.
270                  */
271                 pw_buf->smb_nt_passwd = NULL;
272
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;
279                   }
280                   p += 33; /* Move to the first character of the line after
281                               the NT password. */
282                 }
283
284                 if (*p == '[') {
285         
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;
291
292                   /* Now try and get the last change time. */
293                   if(end_p)
294                     p = end_p + 1;
295                   if(*p == ':') {
296                     p++;
297                     if(*p && (strncasecmp(p, "LCT-", 4)==0)) {
298                       int i;
299                       p += 4;
300                       for(i = 0; i < 8; i++) {
301                         if(p[i] == '\0' || !isxdigit(p[i]))
302                           break;
303                       }
304                       if(i == 8) {
305                         /*
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.
309                          */
310                         pw_buf->pass_last_set_time = (time_t)strtol((char *)p, NULL, 16);
311                       }
312                     }
313                   }
314                 } else {
315                   /* 'Old' style file. Fake up based on user name. */
316                   /*
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.
320                    */
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;
324                   }
325                 }
326
327                 return pw_buf;
328         }
329         return NULL;
330 }
331
332 struct smb_passwd *getsmbfilepwname(struct smb_passwd *pw_buf,const char *fname, const char *name)
333 {
334         FILE *file;
335
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))
340                 /* skip entries */;
341         fclose(file);
342         return pw_buf;
343 }
344
345 void setsmbname(struct smb_passwd *pw_buf,const char *name)
346 {
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;
350 }