minor cleanups
[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 pw_file_lock(char *name, int type, int secs)
62 {
63         int             fd = open(name, O_RDWR | O_CREAT, 0666);
64         if (fd < 0)
65                 return (-1);
66         if (do_pw_lock(fd, secs, type)) {
67                 close(fd);
68                 return -1;
69         }
70         return fd;
71 }
72
73 int pw_file_unlock(int fd)
74 {
75         do_pw_lock(fd, 5, F_UNLCK);
76         return close(fd);
77 }
78
79 /*
80  * Routine to get the next 32 hex characters and turn them
81  * into a 16 byte array.
82  */
83
84 static int gethexpwd(char *p, char *pwd)
85 {
86         int i;
87         unsigned char   lonybble, hinybble;
88         char           *hexchars = "0123456789ABCDEF";
89         char           *p1, *p2;
90
91         for (i = 0; i < 32; i += 2) {
92                 hinybble = toupper(p[i]);
93                 lonybble = toupper(p[i + 1]);
94  
95                 p1 = strchr(hexchars, hinybble);
96                 p2 = strchr(hexchars, lonybble);
97                 if (!p1 || !p2)
98                         return (False);
99                 hinybble = PTR_DIFF(p1, hexchars);
100                 lonybble = PTR_DIFF(p2, hexchars);
101  
102                 pwd[i / 2] = (hinybble << 4) | lonybble;
103         }
104         return (True);
105 }
106
107 /*
108  * Routine to search the smbpasswd file for an entry matching the username.
109  */
110 struct smb_passwd *get_smbpwnam(char *name)
111 {
112         /* Static buffers we will return. */
113         static struct smb_passwd pw_buf;
114         static pstring  user_name;
115         static unsigned char smbpwd[16];
116         static unsigned char smbntpwd[16];
117         char            linebuf[256];
118         char            readbuf[16 * 1024];
119         unsigned char   c;
120         unsigned char  *p;
121         long            uidval;
122         long            linebuf_len;
123         FILE           *fp;
124         int             lockfd;
125         char           *pfile = lp_smb_passwd_file();
126
127         if (!*pfile) {
128                 DEBUG(0, ("No SMB password file set\n"));
129                 return (NULL);
130         }
131         DEBUG(10, ("get_smbpwnam: opening file %s\n", pfile));
132
133         fp = fopen(pfile, "r");
134
135         if (fp == NULL) {
136                 DEBUG(0, ("get_smbpwnam: unable to open file %s\n", pfile));
137                 return NULL;
138         }
139         /* Set a 16k buffer to do more efficient reads */
140         setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
141
142         if ((lockfd = pw_file_lock(pfile, F_RDLCK, 5)) < 0) {
143                 DEBUG(0, ("get_smbpwnam: unable to lock file %s\n", pfile));
144                 fclose(fp);
145                 return NULL;
146         }
147         /* make sure it is only rw by the owner */
148         chmod(pfile, 0600);
149
150         /* We have a read lock on the file. */
151         /*
152          * Scan the file, a line at a time and check if the name matches.
153          */
154         while (!feof(fp)) {
155                 linebuf[0] = '\0';
156
157                 fgets(linebuf, 256, fp);
158                 if (ferror(fp)) {
159                         fclose(fp);
160                         pw_file_unlock(lockfd);
161                         return NULL;
162                 }
163                 /*
164                  * Check if the string is terminated with a newline - if not
165                  * then we must keep reading and discard until we get one.
166                  */
167                 linebuf_len = strlen(linebuf);
168                 if (linebuf[linebuf_len - 1] != '\n') {
169                         c = '\0';
170                         while (!ferror(fp) && !feof(fp)) {
171                                 c = fgetc(fp);
172                                 if (c == '\n')
173                                         break;
174                         }
175                 } else
176                         linebuf[linebuf_len - 1] = '\0';
177
178 #ifdef DEBUG_PASSWORD
179                 DEBUG(100, ("get_smbpwnam: got line |%s|\n", linebuf));
180 #endif
181                 if ((linebuf[0] == 0) && feof(fp)) {
182                         DEBUG(4, ("get_smbpwnam: end of file reached\n"));
183                         break;
184                 }
185                 /*
186                  * The line we have should be of the form :-
187                  * 
188                  * username:uid:[32hex bytes]:....other flags presently
189                  * ignored....
190                  * 
191                  * or,
192                  *
193                  * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
194                  *
195                  * if Windows NT compatible passwords are also present.
196                  */
197
198                 if (linebuf[0] == '#' || linebuf[0] == '\0') {
199                         DEBUG(6, ("get_smbpwnam: skipping comment or blank line\n"));
200                         continue;
201                 }
202                 p = (unsigned char *) strchr(linebuf, ':');
203                 if (p == NULL) {
204                         DEBUG(0, ("get_smbpwnam: malformed password entry (no :)\n"));
205                         continue;
206                 }
207                 /*
208                  * As 256 is shorter than a pstring we don't need to check
209                  * length here - if this ever changes....
210                  */
211                 strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
212                 user_name[PTR_DIFF(p, linebuf)] = '\0';
213                 if (!strequal(user_name, name))
214                         continue;
215
216                 /* User name matches - get uid and password */
217                 p++;            /* Go past ':' */
218                 if (!isdigit(*p)) {
219                         DEBUG(0, ("get_smbpwnam: malformed password entry (uid not number)\n"));
220                         fclose(fp);
221                         pw_file_unlock(lockfd);
222                         return NULL;
223                 }
224                 uidval = atoi((char *) p);
225                 while (*p && isdigit(*p))
226                         p++;
227                 if (*p != ':') {
228                         DEBUG(0, ("get_smbpwnam: malformed password entry (no : after uid)\n"));
229                         fclose(fp);
230                         pw_file_unlock(lockfd);
231                         return NULL;
232                 }
233                 /*
234                  * Now get the password value - this should be 32 hex digits
235                  * which are the ascii representations of a 16 byte string.
236                  * Get two at a time and put them into the password.
237                  */
238                 p++;
239                 if (*p == '*' || *p == 'X') {
240                         /* Password deliberately invalid - end here. */
241                         DEBUG(10, ("get_smbpwnam: entry invalidated for user %s\n", user_name));
242                         fclose(fp);
243                         pw_file_unlock(lockfd);
244                         return NULL;
245                 }
246                 if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) {
247                         DEBUG(0, ("get_smbpwnam: malformed password entry (passwd too short)\n"));
248                         fclose(fp);
249                         pw_file_unlock(lockfd);
250                         return (False);
251                 }
252                 if (p[32] != ':') {
253                         DEBUG(0, ("get_smbpwnam: malformed password entry (no terminating :)\n"));
254                         fclose(fp);
255                         pw_file_unlock(lockfd);
256                         return NULL;
257                 }
258                 if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
259                         pw_buf.smb_passwd = NULL;
260                 } else {
261                         if(!gethexpwd((char *)p,(char *)smbpwd)) {
262                                 DEBUG(0, ("Malformed Lanman password entry (non hex chars)\n"));
263                                 fclose(fp);
264                                 pw_file_unlock(lockfd);
265                                 return NULL;
266                         }
267                         pw_buf.smb_passwd = smbpwd;
268                 }
269                 pw_buf.smb_name = user_name;
270                 pw_buf.smb_userid = uidval;
271                 pw_buf.smb_nt_passwd = NULL;
272
273                 /* Now check if the NT compatible password is
274                         available. */
275                 p += 33; /* Move to the first character of the line after
276                                         the lanman password. */
277                 if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
278                         if (*p != '*' && *p != 'X') {
279                                 if(gethexpwd((char *)p,(char *)smbntpwd))
280                                         pw_buf.smb_nt_passwd = smbntpwd;
281                         }
282                 }
283
284                 fclose(fp);
285                 pw_file_unlock(lockfd);
286                 DEBUG(5, ("get_smbpwname: returning passwd entry for user %s, uid %d\n",
287                           user_name, uidval));
288                 return &pw_buf;
289         }
290
291         fclose(fp);
292         pw_file_unlock(lockfd);
293         return NULL;
294 }
295 #else
296  void smbpass_dummy(void)
297 {
298 }                               /* To avoid compiler complaints */
299 #endif