1f880881bc5aa0990659b05e3ef2bcf9b0c40555
[kai/samba.git] / source / passdb / smbpass.c
1 #ifdef SMB_PASSWD
2 /*
3  * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup
4  * Copyright (C) Andrew Tridgell 1992-1995 Modified by Jeremy Allison 1995.
5  * 
6  * This program is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  * 
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with
17  * this program; if not, write to the Free Software Foundation, Inc., 675
18  * Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "includes.h"
22
23 extern int      DEBUGLEVEL;
24
25 int             gotalarm;
26
27 void 
28 gotalarm_sig()
29 {
30         gotalarm = 1;
31 }
32
33 int 
34 do_pw_lock(int fd, int waitsecs, int type)
35 {
36         struct flock    lock;
37         int             ret;
38
39         gotalarm = 0;
40         signal(SIGALRM, SIGNAL_CAST gotalarm_sig);
41
42         lock.l_type = type;
43         lock.l_whence = SEEK_SET;
44         lock.l_start = 0;
45         lock.l_len = 1;
46         lock.l_pid = 0;
47
48         alarm(5);
49         ret = fcntl(fd, F_SETLKW, &lock);
50         alarm(0);
51         signal(SIGALRM, SIGNAL_CAST SIG_DFL);
52
53         if (gotalarm) {
54                 DEBUG(0, ("do_pw_lock: failed to %s SMB passwd file.\n",
55                           type == F_UNLCK ? "unlock" : "lock"));
56                 return -1;
57         }
58         return ret;
59 }
60
61 int 
62 pw_file_lock(char *name, int type, int secs)
63 {
64         int             fd = open(name, O_RDWR | O_CREAT, 0666);
65         if (fd < 0)
66                 return (-1);
67         if (do_pw_lock(fd, secs, type)) {
68                 close(fd);
69                 return -1;
70         }
71         return fd;
72 }
73
74 int 
75 pw_file_unlock(int fd)
76 {
77         do_pw_lock(fd, 5, F_UNLCK);
78         return close(fd);
79 }
80
81 /*
82  * Routine to get the next 32 hex characters and turn them
83  * into a 16 byte array.
84  */
85
86 static int gethexpwd(char *p, char *pwd)
87 {
88         int i;
89         unsigned char   lonybble, hinybble;
90         char           *hexchars = "0123456789ABCDEF";
91         char           *p1, *p2;
92
93         for (i = 0; i < 32; i += 2) {
94                 hinybble = toupper(p[i]);
95                 lonybble = toupper(p[i + 1]);
96  
97                 p1 = strchr(hexchars, hinybble);
98                 p2 = strchr(hexchars, lonybble);
99                 if (!p1 || !p2)
100                         return (False);
101                 hinybble = PTR_DIFF(p1, hexchars);
102                 lonybble = PTR_DIFF(p2, hexchars);
103  
104                 pwd[i / 2] = (hinybble << 4) | lonybble;
105         }
106         return (True);
107 }
108
109 /*
110  * Routine to search the smbpasswd file for an entry matching the username.
111  */
112 struct smb_passwd *get_smbpwnam(char *name)
113 {
114         /* Static buffers we will return. */
115         static struct smb_passwd pw_buf;
116         static pstring  user_name;
117         static unsigned char smbpwd[16];
118         static unsigned char smbntpwd[16];
119         char            linebuf[256];
120         char            readbuf[16 * 1024];
121         unsigned char   c;
122         unsigned char  *p;
123         long            uidval;
124         long            linebuf_len;
125         FILE           *fp;
126         int             lockfd;
127         char           *pfile = lp_smb_passwd_file();
128
129         if (!*pfile) {
130                 DEBUG(0, ("No SMB password file set\n"));
131                 return (NULL);
132         }
133         DEBUG(10, ("get_smbpwnam: opening file %s\n", pfile));
134
135         fp = fopen(pfile, "r");
136
137         if (fp == NULL) {
138                 DEBUG(0, ("get_smbpwnam: unable to open file %s\n", pfile));
139                 return NULL;
140         }
141         /* Set a 16k buffer to do more efficient reads */
142         setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
143
144         if ((lockfd = pw_file_lock(pfile, F_RDLCK, 5)) < 0) {
145                 DEBUG(0, ("get_smbpwnam: unable to lock file %s\n", pfile));
146                 fclose(fp);
147                 return NULL;
148         }
149         /* make sure it is only rw by the owner */
150         chmod(pfile, 0600);
151
152         /* We have a read lock on the file. */
153         /*
154          * Scan the file, a line at a time and check if the name matches.
155          */
156         while (!feof(fp)) {
157                 linebuf[0] = '\0';
158
159                 fgets(linebuf, 256, fp);
160                 if (ferror(fp)) {
161                         fclose(fp);
162                         pw_file_unlock(lockfd);
163                         return NULL;
164                 }
165                 /*
166                  * Check if the string is terminated with a newline - if not
167                  * then we must keep reading and discard until we get one.
168                  */
169                 linebuf_len = strlen(linebuf);
170                 if (linebuf[linebuf_len - 1] != '\n') {
171                         c = '\0';
172                         while (!ferror(fp) && !feof(fp)) {
173                                 c = fgetc(fp);
174                                 if (c == '\n')
175                                         break;
176                         }
177                 } else
178                         linebuf[linebuf_len - 1] = '\0';
179
180 #ifdef DEBUG_PASSWORD
181                 DEBUG(100, ("get_smbpwnam: got line |%s|\n", linebuf));
182 #endif
183                 if ((linebuf[0] == 0) && feof(fp)) {
184                         DEBUG(4, ("get_smbpwnam: end of file reached\n"));
185                         break;
186                 }
187                 /*
188                  * The line we have should be of the form :-
189                  * 
190                  * username:uid:[32hex bytes]:....other flags presently
191                  * ignored....
192                  * 
193                  * or,
194                  *
195                  * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
196                  *
197                  * if Windows NT compatible passwords are also present.
198                  */
199
200                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
201                         DEBUG(6, ("get_smbpwnam: skipping comment or blank line\n"));
202                         continue;
203                 }
204                 p = (unsigned char *) strchr(linebuf, ':');
205                 if (p == NULL) {
206                         DEBUG(0, ("get_smbpwnam: malformed password entry (no :)\n"));
207                         continue;
208                 }
209                 /*
210                  * As 256 is shorter than a pstring we don't need to check
211                  * length here - if this ever changes....
212                  */
213                 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
214                 user_name[PTR_DIFF(p, linebuf)] = '\0';
215                 if (!strequal(user_name, name))
216                         continue;
217
218                 /* User name matches - get uid and password */
219                 p++;            /* Go past ':' */
220                 if (!isdigit(*p)) {
221                         DEBUG(0, ("get_smbpwnam: malformed password entry (uid not number)\n"));
222                         fclose(fp);
223                         pw_file_unlock(lockfd);
224                         return NULL;
225                 }
226                 uidval = atoi((char *) p);
227                 while (*p && isdigit(*p))
228                         p++;
229                 if (*p != ':') {
230                         DEBUG(0, ("get_smbpwnam: malformed password entry (no : after uid)\n"));
231                         fclose(fp);
232                         pw_file_unlock(lockfd);
233                         return NULL;
234                 }
235                 /*
236                  * Now get the password value - this should be 32 hex digits
237                  * which are the ascii representations of a 16 byte string.
238                  * Get two at a time and put them into the password.
239                  */
240                 p++;
241                 if (*p == '*' || *p == 'X') {
242                         /* Password deliberately invalid - end here. */
243                         DEBUG(10, ("get_smbpwnam: entry invalidated for user %s\n", user_name));
244                         fclose(fp);
245                         pw_file_unlock(lockfd);
246                         return NULL;
247                 }
248                 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
249                         DEBUG(0, ("get_smbpwnam: malformed password entry (passwd too short)\n"));
250                         fclose(fp);
251                         pw_file_unlock(lockfd);
252                         return (False);
253                 }
254                 if (p[32] != ':') {
255                         DEBUG(0, ("get_smbpwnam: malformed password entry (no terminating :)\n"));
256                         fclose(fp);
257                         pw_file_unlock(lockfd);
258                         return NULL;
259                 }
260                 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
261                         pw_buf.smb_passwd = NULL;
262                 } else {
263                         if(!gethexpwd((char *)p,(char *)smbpwd)) {
264                                 DEBUG(0, ("Malformed Lanman password entry (non hex chars)\n"));
265                                 fclose(fp);
266                                 pw_file_unlock(lockfd);
267                                 return NULL;
268                         }
269                         pw_buf.smb_passwd = smbpwd;
270                 }
271                 pw_buf.smb_name = user_name;
272                 pw_buf.smb_userid = uidval;
273                 pw_buf.smb_nt_passwd = NULL;
274
275                 /* Now check if the NT compatible password is
276                         available. */
277                 p += 33; /* Move to the first character of the line after
278                                         the lanman password. */
279                 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
280                         if (*p != '*' && *p != 'X') {
281                                 if(gethexpwd((char *)p,(char *)smbntpwd))
282                                         pw_buf.smb_nt_passwd = smbntpwd;
283                         }
284                 }
285
286                 fclose(fp);
287                 pw_file_unlock(lockfd);
288                 DEBUG(5, ("get_smbpwname: returning passwd entry for user %s, uid %d\n",
289                           user_name, uidval));
290                 return &pw_buf;
291         }
292
293         fclose(fp);
294         pw_file_unlock(lockfd);
295         return NULL;
296 }
297 #else
298  void smbpass_dummy(void)
299 {
300 }                               /* To avoid compiler complaints */
301 #endif