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