1/*	$NetBSD: ruserpass.c,v 1.29 2003/08/07 11:13:57 agc Exp $	*/
2
3/*
4 * Copyright (c) 1985, 1993, 1994
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33
34#include <sys/types.h>
35#include <sys/stat.h>
36
37#include <ctype.h>
38#include <err.h>
39#include <errno.h>
40#include <netdb.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#include "ftp_var.h"
47
48static	int token(void);
49static	FILE *cfile;
50
51#define	DEFAULT	1
52#define	LOGIN	2
53#define	PASSWD	3
54#define	ACCOUNT	4
55#define	MACDEF	5
56#define	ID	10
57#define	MACH	11
58
59static char tokval[100];
60
61static struct toktab {
62	char *tokstr;
63	int tval;
64} toktab[] = {
65	{ "default",	DEFAULT },
66	{ "login",	LOGIN },
67	{ "password",	PASSWD },
68	{ "passwd",	PASSWD },
69	{ "account",	ACCOUNT },
70	{ "machine",	MACH },
71	{ "macdef",	MACDEF },
72	{ NULL,		0 }
73};
74
75int
76ruserpass(const char *host, const char **aname, const char **apass,
77	const char **aacct)
78{
79	char *tmp;
80	char myname[MAXHOSTNAMELEN + 1], *mydomain;
81	int t, i, c, usedefault = 0;
82	struct stat stb;
83
84	if (netrc[0] == '\0')
85		return (0);
86	cfile = fopen(netrc, "r");
87	if (cfile == NULL) {
88		if (errno != ENOENT)
89			warn("%s", netrc);
90		return (0);
91	}
92	if (gethostname(myname, sizeof(myname)) < 0)
93		myname[0] = '\0';
94	myname[sizeof(myname) - 1] = '\0';
95	if ((mydomain = strchr(myname, '.')) == NULL)
96		mydomain = "";
97 next:
98	while ((t = token())) switch(t) {
99
100	case DEFAULT:
101		usedefault = 1;
102		/* FALL THROUGH */
103
104	case MACH:
105		if (!usedefault) {
106			if (token() != ID)
107				continue;
108			/*
109			 * Allow match either for user's input host name
110			 * or official hostname.  Also allow match of
111			 * incompletely-specified host in local domain.
112			 */
113			if (strcasecmp(host, tokval) == 0)
114				goto match;
115			if (strcasecmp(hostname, tokval) == 0)
116				goto match;
117			if ((tmp = strchr(hostname, '.')) != NULL &&
118			    strcasecmp(tmp, mydomain) == 0 &&
119			    strncasecmp(hostname, tokval, tmp-hostname) == 0 &&
120			    tokval[tmp - hostname] == '\0')
121				goto match;
122			if ((tmp = strchr(host, '.')) != NULL &&
123			    strcasecmp(tmp, mydomain) == 0 &&
124			    strncasecmp(host, tokval, tmp - host) == 0 &&
125			    tokval[tmp - host] == '\0')
126				goto match;
127			continue;
128		}
129	match:
130		while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
131
132		case LOGIN:
133			if (token()) {
134				if (*aname == NULL)
135					*aname = xstrdup(tokval);
136				else {
137					if (strcmp(*aname, tokval))
138						goto next;
139				}
140			}
141			break;
142		case PASSWD:
143			if ((*aname == NULL || strcmp(*aname, "anonymous")) &&
144			    fstat(fileno(cfile), &stb) >= 0 &&
145			    (stb.st_mode & 077) != 0) {
146	warnx("Error: .netrc file is readable by others.");
147	warnx("Remove password or make file unreadable by others.");
148				goto bad;
149			}
150			if (token() && *apass == NULL)
151				*apass = xstrdup(tokval);
152			break;
153		case ACCOUNT:
154			if (fstat(fileno(cfile), &stb) >= 0
155			    && (stb.st_mode & 077) != 0) {
156	warnx("Error: .netrc file is readable by others.");
157	warnx("Remove account or make file unreadable by others.");
158				goto bad;
159			}
160			if (token() && *aacct == NULL)
161				*aacct = xstrdup(tokval);
162			break;
163		case MACDEF:
164			if (proxy) {
165				(void)fclose(cfile);
166				return (0);
167			}
168			while ((c = getc(cfile)) != EOF)
169				if (c != ' ' && c != '\t')
170					break;
171			if (c == EOF || c == '\n') {
172				fputs("Missing macdef name argument.\n",
173				    ttyout);
174				goto bad;
175			}
176			if (macnum == 16) {
177				fputs(
178			    "Limit of 16 macros have already been defined.\n",
179				    ttyout);
180				goto bad;
181			}
182			tmp = macros[macnum].mac_name;
183			*tmp++ = c;
184			for (i = 0; i < 8 && (c = getc(cfile)) != EOF &&
185			    !isspace(c); ++i) {
186				*tmp++ = c;
187			}
188			if (c == EOF) {
189				fputs(
190			    "Macro definition missing null line terminator.\n",
191				    ttyout);
192				goto bad;
193			}
194			*tmp = '\0';
195			if (c != '\n') {
196				while ((c = getc(cfile)) != EOF && c != '\n');
197			}
198			if (c == EOF) {
199				fputs(
200			    "Macro definition missing null line terminator.\n",
201				    ttyout);
202				goto bad;
203			}
204			if (macnum == 0) {
205				macros[macnum].mac_start = macbuf;
206			}
207			else {
208				macros[macnum].mac_start =
209				    macros[macnum-1].mac_end + 1;
210			}
211			tmp = macros[macnum].mac_start;
212			while (tmp != macbuf + 4096) {
213				if ((c = getc(cfile)) == EOF) {
214					fputs(
215			    "Macro definition missing null line terminator.\n",
216					    ttyout);
217					goto bad;
218				}
219				*tmp = c;
220				if (*tmp == '\n') {
221					if (*(tmp-1) == '\0') {
222					   macros[macnum++].mac_end = tmp - 1;
223					   break;
224					}
225					*tmp = '\0';
226				}
227				tmp++;
228			}
229			if (tmp == macbuf + 4096) {
230				fputs("4K macro buffer exceeded.\n",
231				    ttyout);
232				goto bad;
233			}
234			break;
235		default:
236			warnx("Unknown .netrc keyword %s", tokval);
237			break;
238		}
239		goto done;
240	}
241 done:
242	(void)fclose(cfile);
243	return (0);
244 bad:
245	(void)fclose(cfile);
246	return (-1);
247}
248
249static int
250token(void)
251{
252	char *cp;
253	int c;
254	struct toktab *t;
255
256	if (feof(cfile) || ferror(cfile))
257		return (0);
258	while ((c = getc(cfile)) != EOF &&
259	    (c == '\n' || c == '\t' || c == ' ' || c == ','))
260		continue;
261	if (c == EOF)
262		return (0);
263	cp = tokval;
264	if (c == '"') {
265		while ((c = getc(cfile)) != EOF && c != '"') {
266			if (c == '\\')
267				c = getc(cfile);
268			*cp++ = c;
269		}
270	} else {
271		*cp++ = c;
272		while ((c = getc(cfile)) != EOF
273		    && c != '\n' && c != '\t' && c != ' ' && c != ',') {
274			if (c == '\\')
275				c = getc(cfile);
276			*cp++ = c;
277		}
278	}
279	*cp = 0;
280	if (tokval[0] == 0)
281		return (0);
282	for (t = toktab; t->tokstr; t++)
283		if (!strcmp(t->tokstr, tokval))
284			return (t->tval);
285	return (ID);
286}
287