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