xref: /netbsd-src/usr.bin/ftp/ruserpass.c (revision d1063cbfa444bde41b012f5d034d3349cbd4ddc6)
1*d1063cbfSchristos /*	$NetBSD: ruserpass.c,v 1.35 2024/10/04 18:04:06 christos Exp $	*/
2917ef72dStls 
361f28255Scgd /*
4dea9ded2Scgd  * Copyright (c) 1985, 1993, 1994
5dea9ded2Scgd  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
1589aaa1bbSagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
32b9d5554dSlukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
347a7fa807Slukem #if 0
35917ef72dStls static char sccsid[] = "@(#)ruserpass.c	8.4 (Berkeley) 4/27/95";
367a7fa807Slukem #else
37*d1063cbfSchristos __RCSID("$NetBSD: ruserpass.c,v 1.35 2024/10/04 18:04:06 christos Exp $");
387a7fa807Slukem #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
4161f28255Scgd #include <sys/types.h>
4261f28255Scgd #include <sys/stat.h>
43dea9ded2Scgd 
44dea9ded2Scgd #include <ctype.h>
45dea9ded2Scgd #include <err.h>
4661f28255Scgd #include <errno.h>
470a0b6ed3Slukem #include <netdb.h>
48dea9ded2Scgd #include <stdio.h>
49dea9ded2Scgd #include <stdlib.h>
50dea9ded2Scgd #include <string.h>
51dea9ded2Scgd #include <unistd.h>
52dea9ded2Scgd 
5361f28255Scgd #include "ftp_var.h"
5461f28255Scgd 
552c9a4cf5Slukem static	int token(void);
5661f28255Scgd static	FILE *cfile;
5761f28255Scgd 
5861f28255Scgd #define	DEFAULT	1
5961f28255Scgd #define	LOGIN	2
6061f28255Scgd #define	PASSWD	3
6161f28255Scgd #define	ACCOUNT	4
6261f28255Scgd #define	MACDEF	5
6361f28255Scgd #define	ID	10
6461f28255Scgd #define	MACH	11
6561f28255Scgd 
6661f28255Scgd static char tokval[100];
6761f28255Scgd 
6861f28255Scgd static struct toktab {
69ddc3d4caSlukem 	const char *tokstr;
7061f28255Scgd 	int tval;
7161f28255Scgd } toktab[] = {
72dea9ded2Scgd 	{ "default",	DEFAULT },
73dea9ded2Scgd 	{ "login",	LOGIN },
74dea9ded2Scgd 	{ "password",	PASSWD },
75dea9ded2Scgd 	{ "passwd",	PASSWD },
76dea9ded2Scgd 	{ "account",	ACCOUNT },
77dea9ded2Scgd 	{ "machine",	MACH },
78dea9ded2Scgd 	{ "macdef",	MACDEF },
79dea9ded2Scgd 	{ NULL,		0 }
8061f28255Scgd };
8161f28255Scgd 
82*d1063cbfSchristos static int
83*d1063cbfSchristos match_host_domain(const char *host, const char *domain, const char *tokv)
84*d1063cbfSchristos {
85*d1063cbfSchristos 	const char *tmp;
86*d1063cbfSchristos 
87*d1063cbfSchristos 	if (strcasecmp(host, tokval) == 0)
88*d1063cbfSchristos 		return 1;
89*d1063cbfSchristos 
90*d1063cbfSchristos 	return (tmp = strchr(host, '.')) != NULL &&
91*d1063cbfSchristos 	    strcasecmp(tmp, domain) == 0 &&
92*d1063cbfSchristos 	    strncasecmp(host, tokv, tmp - host) == 0 &&
93*d1063cbfSchristos 	    tokv[tmp - host] == '\0';
94*d1063cbfSchristos }
95*d1063cbfSchristos 
96dea9ded2Scgd int
97ddc3d4caSlukem ruserpass(const char *host, char **aname, char **apass, char **aacct)
9861f28255Scgd {
99ecd3d780Slukem 	char *tmp;
100ddc3d4caSlukem 	const char *mydomain;
101ddc3d4caSlukem 	char myname[MAXHOSTNAMELEN + 1];
10261f28255Scgd 	int t, i, c, usedefault = 0;
10361f28255Scgd 	struct stat stb;
10461f28255Scgd 
105ecd3d780Slukem 	if (netrc[0] == '\0')
1068ef5f804Slukem 		return (0);
107ecd3d780Slukem 	cfile = fopen(netrc, "r");
10861f28255Scgd 	if (cfile == NULL) {
10961f28255Scgd 		if (errno != ENOENT)
1108a06b9bfSlukem 			warn("Can't read `%s'", netrc);
11161f28255Scgd 		return (0);
11261f28255Scgd 	}
11361f28255Scgd 	if (gethostname(myname, sizeof(myname)) < 0)
11461f28255Scgd 		myname[0] = '\0';
1152beab49aSmrg 	myname[sizeof(myname) - 1] = '\0';
116dea9ded2Scgd 	if ((mydomain = strchr(myname, '.')) == NULL)
11761f28255Scgd 		mydomain = "";
11861f28255Scgd  next:
119b1e08b00Slukem 	while ((t = token()) > 0) switch(t) {
12061f28255Scgd 
12161f28255Scgd 	case DEFAULT:
12261f28255Scgd 		usedefault = 1;
12361f28255Scgd 		/* FALL THROUGH */
12461f28255Scgd 
12561f28255Scgd 	case MACH:
12661f28255Scgd 		if (!usedefault) {
127b1e08b00Slukem 			if ((t = token()) == -1)
128b1e08b00Slukem 				goto bad;
129b1e08b00Slukem 			if (t != ID)
13061f28255Scgd 				continue;
13161f28255Scgd 			/*
13261f28255Scgd 			 * Allow match either for user's input host name
13361f28255Scgd 			 * or official hostname.  Also allow match of
13461f28255Scgd 			 * incompletely-specified host in local domain.
13561f28255Scgd 			 */
136*d1063cbfSchristos 			if (match_host_domain(hostname, mydomain, tokval))
13761f28255Scgd 				goto match;
138*d1063cbfSchristos 			if (match_host_domain(host, mydomain, tokval))
13961f28255Scgd 				goto match;
14061f28255Scgd 			continue;
14161f28255Scgd 		}
14261f28255Scgd 	match:
143b1e08b00Slukem 		while ((t = token()) > 0 &&
144b1e08b00Slukem 		    t != MACH && t != DEFAULT) switch(t) {
14561f28255Scgd 
14661f28255Scgd 		case LOGIN:
147b1e08b00Slukem 			if ((t = token()) == -1)
148b1e08b00Slukem 				goto bad;
149b1e08b00Slukem 			if (t) {
150d067f3bbSlukem 				if (*aname == NULL)
151e2279410Schristos 					*aname = ftp_strdup(tokval);
1524a8ec549Slukem 				else {
15361f28255Scgd 					if (strcmp(*aname, tokval))
15461f28255Scgd 						goto next;
15561f28255Scgd 				}
156a0a2de67Schristos 			}
15761f28255Scgd 			break;
15861f28255Scgd 		case PASSWD:
159917ef72dStls 			if ((*aname == NULL || strcmp(*aname, "anonymous")) &&
16061f28255Scgd 			    fstat(fileno(cfile), &stb) >= 0 &&
16161f28255Scgd 			    (stb.st_mode & 077) != 0) {
1628a06b9bfSlukem 	warnx("Error: .netrc file is readable by others");
1638a06b9bfSlukem 	warnx("Remove password or make file unreadable by others");
16461f28255Scgd 				goto bad;
16561f28255Scgd 			}
166b1e08b00Slukem 			if ((t = token()) == -1)
167b1e08b00Slukem 				goto bad;
168b1e08b00Slukem 			if (t && *apass == NULL)
169e2279410Schristos 				*apass = ftp_strdup(tokval);
17061f28255Scgd 			break;
17161f28255Scgd 		case ACCOUNT:
17261f28255Scgd 			if (fstat(fileno(cfile), &stb) >= 0
17361f28255Scgd 			    && (stb.st_mode & 077) != 0) {
1748a06b9bfSlukem 	warnx("Error: .netrc file is readable by others");
1758a06b9bfSlukem 	warnx("Remove account or make file unreadable by others");
17661f28255Scgd 				goto bad;
17761f28255Scgd 			}
178b1e08b00Slukem 			if ((t = token()) == -1)
179b1e08b00Slukem 				goto bad;
180b1e08b00Slukem 			if (t && *aacct == NULL)
181e2279410Schristos 				*aacct = ftp_strdup(tokval);
18261f28255Scgd 			break;
18361f28255Scgd 		case MACDEF:
18461f28255Scgd 			if (proxy) {
18561f28255Scgd 				(void)fclose(cfile);
18661f28255Scgd 				return (0);
18761f28255Scgd 			}
1887a7fa807Slukem 			while ((c = getc(cfile)) != EOF)
1897a7fa807Slukem 				if (c != ' ' && c != '\t')
1907a7fa807Slukem 					break;
19161f28255Scgd 			if (c == EOF || c == '\n') {
1929a6e9b2cSlukem 				fputs("Missing macdef name argument.\n",
1939a6e9b2cSlukem 				    ttyout);
19461f28255Scgd 				goto bad;
19561f28255Scgd 			}
19661f28255Scgd 			if (macnum == 16) {
1979a6e9b2cSlukem 				fputs(
1989a6e9b2cSlukem 			    "Limit of 16 macros have already been defined.\n",
1999a6e9b2cSlukem 				    ttyout);
20061f28255Scgd 				goto bad;
20161f28255Scgd 			}
20261f28255Scgd 			tmp = macros[macnum].mac_name;
20361f28255Scgd 			*tmp++ = c;
20461f28255Scgd 			for (i = 0; i < 8 && (c = getc(cfile)) != EOF &&
20561f28255Scgd 			    !isspace(c); ++i) {
20661f28255Scgd 				*tmp++ = c;
20761f28255Scgd 			}
20861f28255Scgd 			if (c == EOF) {
2099a6e9b2cSlukem 				fputs(
2109a6e9b2cSlukem 			    "Macro definition missing null line terminator.\n",
2119a6e9b2cSlukem 				    ttyout);
21261f28255Scgd 				goto bad;
21361f28255Scgd 			}
21461f28255Scgd 			*tmp = '\0';
21561f28255Scgd 			if (c != '\n') {
21661f28255Scgd 				while ((c = getc(cfile)) != EOF && c != '\n');
21761f28255Scgd 			}
21861f28255Scgd 			if (c == EOF) {
2199a6e9b2cSlukem 				fputs(
2209a6e9b2cSlukem 			    "Macro definition missing null line terminator.\n",
2219a6e9b2cSlukem 				    ttyout);
22261f28255Scgd 				goto bad;
22361f28255Scgd 			}
22461f28255Scgd 			if (macnum == 0) {
22561f28255Scgd 				macros[macnum].mac_start = macbuf;
22661f28255Scgd 			}
22761f28255Scgd 			else {
228b6df1b3dSlukem 				macros[macnum].mac_start =
229b6df1b3dSlukem 				    macros[macnum-1].mac_end + 1;
23061f28255Scgd 			}
23161f28255Scgd 			tmp = macros[macnum].mac_start;
23261f28255Scgd 			while (tmp != macbuf + 4096) {
23361f28255Scgd 				if ((c = getc(cfile)) == EOF) {
2349a6e9b2cSlukem 					fputs(
2359a6e9b2cSlukem 			    "Macro definition missing null line terminator.\n",
2369a6e9b2cSlukem 					    ttyout);
23761f28255Scgd 					goto bad;
23861f28255Scgd 				}
23961f28255Scgd 				*tmp = c;
24061f28255Scgd 				if (*tmp == '\n') {
241b1e08b00Slukem 					if (tmp == macros[macnum].mac_start) {
242b1e08b00Slukem 						macros[macnum++].mac_end = tmp;
243b1e08b00Slukem 						break;
244b1e08b00Slukem 					} else if (*(tmp - 1) == '\0') {
245b1e08b00Slukem 						macros[macnum++].mac_end =
246b1e08b00Slukem 						    tmp - 1;
24761f28255Scgd 						break;
24861f28255Scgd 					}
24961f28255Scgd 					*tmp = '\0';
25061f28255Scgd 				}
25161f28255Scgd 				tmp++;
25261f28255Scgd 			}
25361f28255Scgd 			if (tmp == macbuf + 4096) {
254e83274d0Slukem 				fputs("4 KiB macro buffer exceeded.\n",
2559a6e9b2cSlukem 				    ttyout);
25661f28255Scgd 				goto bad;
25761f28255Scgd 			}
25861f28255Scgd 			break;
25961f28255Scgd 		default:
2608a06b9bfSlukem 			warnx("Unknown .netrc keyword `%s'", tokval);
26161f28255Scgd 			break;
26261f28255Scgd 		}
26361f28255Scgd 		goto done;
26461f28255Scgd 	}
26561f28255Scgd  done:
266b1e08b00Slukem 	if (t == -1)
267b1e08b00Slukem 		goto bad;
26861f28255Scgd 	(void)fclose(cfile);
26961f28255Scgd 	return (0);
27061f28255Scgd  bad:
27161f28255Scgd 	(void)fclose(cfile);
27261f28255Scgd 	return (-1);
27361f28255Scgd }
27461f28255Scgd 
275dea9ded2Scgd static int
2762c9a4cf5Slukem token(void)
27761f28255Scgd {
27861f28255Scgd 	char *cp;
27961f28255Scgd 	int c;
28061f28255Scgd 	struct toktab *t;
28161f28255Scgd 
282dea9ded2Scgd 	if (feof(cfile) || ferror(cfile))
28361f28255Scgd 		return (0);
28461f28255Scgd 	while ((c = getc(cfile)) != EOF &&
28561f28255Scgd 	    (c == '\n' || c == '\t' || c == ' ' || c == ','))
28661f28255Scgd 		continue;
28761f28255Scgd 	if (c == EOF)
28861f28255Scgd 		return (0);
28961f28255Scgd 	cp = tokval;
29061f28255Scgd 	if (c == '"') {
29161f28255Scgd 		while ((c = getc(cfile)) != EOF && c != '"') {
29261f28255Scgd 			if (c == '\\')
293b1e08b00Slukem 				if ((c = getc(cfile)) == EOF)
294b1e08b00Slukem 					break;
29561f28255Scgd 			*cp++ = c;
296b1e08b00Slukem 			if (cp == tokval + sizeof(tokval)) {
297b1e08b00Slukem 				warnx("Token in .netrc too long");
298b1e08b00Slukem 				return (-1);
299b1e08b00Slukem 			}
30061f28255Scgd 		}
30161f28255Scgd 	} else {
30261f28255Scgd 		*cp++ = c;
30361f28255Scgd 		while ((c = getc(cfile)) != EOF
30461f28255Scgd 		    && c != '\n' && c != '\t' && c != ' ' && c != ',') {
30561f28255Scgd 			if (c == '\\')
306b1e08b00Slukem 				if ((c = getc(cfile)) == EOF)
307b1e08b00Slukem 					break;
30861f28255Scgd 			*cp++ = c;
309b1e08b00Slukem 			if (cp == tokval + sizeof(tokval)) {
310b1e08b00Slukem 				warnx("Token in .netrc too long");
311b1e08b00Slukem 				return (-1);
312b1e08b00Slukem 			}
31361f28255Scgd 		}
31461f28255Scgd 	}
31561f28255Scgd 	*cp = 0;
31661f28255Scgd 	if (tokval[0] == 0)
31761f28255Scgd 		return (0);
31861f28255Scgd 	for (t = toktab; t->tokstr; t++)
31961f28255Scgd 		if (!strcmp(t->tokstr, tokval))
32061f28255Scgd 			return (t->tval);
32161f28255Scgd 	return (ID);
32261f28255Scgd }
323