xref: /openbsd-src/usr.bin/ftp/cookie.c (revision bb1c3b3ff7308a1eedba8dc4780f76819332dbe8)
1*bb1c3b3fSnaddy /*	$OpenBSD: cookie.c,v 1.10 2021/02/16 16:27:34 naddy Exp $	*/
2bfd817adSflorian 
3bfd817adSflorian /*
4bfd817adSflorian  * Copyright (c) 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
5bfd817adSflorian  *
6bfd817adSflorian  * Permission to use, copy, modify, and distribute this software for any
7bfd817adSflorian  * purpose with or without fee is hereby granted, provided that the above
8bfd817adSflorian  * copyright notice and this permission notice appear in all copies.
9bfd817adSflorian  *
10bfd817adSflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11bfd817adSflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12bfd817adSflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13bfd817adSflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14bfd817adSflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15bfd817adSflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16bfd817adSflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17bfd817adSflorian  */
18bfd817adSflorian 
19bfd817adSflorian #ifndef NOSSL
20bfd817adSflorian 
21bfd817adSflorian #include <sys/types.h>
22bfd817adSflorian #include <sys/queue.h>
23bfd817adSflorian 
24bfd817adSflorian #include <err.h>
25bfd817adSflorian #include <errno.h>
26bfd817adSflorian #include <fnmatch.h>
27bfd817adSflorian #include <stdio.h>
28bfd817adSflorian #include <string.h>
29bfd817adSflorian #include <stdlib.h>
30bfd817adSflorian #include <time.h>
31bfd817adSflorian 
32bfd817adSflorian #include "ftp_var.h"
33bfd817adSflorian 
34bfd817adSflorian struct cookie {
35bfd817adSflorian 	TAILQ_ENTRY(cookie)	 entry;
36bfd817adSflorian 	TAILQ_ENTRY(cookie)	 tempentry;
37bfd817adSflorian 	u_int8_t		 flags;
38bfd817adSflorian #define F_SECURE		 0x01
39bfd817adSflorian #define F_TAILMATCH		 0x02
40bfd817adSflorian #define F_NOEXPIRY		 0x04
41bfd817adSflorian #define F_MATCHPATH		 0x08
42bfd817adSflorian 	time_t			 expires;
43bfd817adSflorian 	char			*domain;
44bfd817adSflorian 	char			*path;
45bfd817adSflorian 	char			*key;
46bfd817adSflorian 	char			*val;
47bfd817adSflorian };
48bfd817adSflorian TAILQ_HEAD(cookiejar, cookie);
49bfd817adSflorian 
50bfd817adSflorian typedef enum {
51bfd817adSflorian 	DOMAIN = 0, TAILMATCH = 1, PATH = 2, SECURE = 3,
52bfd817adSflorian 	EXPIRES = 4, NAME = 5, VALUE = 6, DONE = 7
53bfd817adSflorian } field_t;
54bfd817adSflorian 
55bfd817adSflorian static struct cookiejar jar;
56bfd817adSflorian 
57bfd817adSflorian void
cookie_load(void)58bfd817adSflorian cookie_load(void)
59bfd817adSflorian {
60bfd817adSflorian 	field_t		 field;
61bfd817adSflorian 	time_t		 date;
62bfd817adSflorian 	char		*line;
63*bb1c3b3fSnaddy 	char		*lbuf = NULL;
64*bb1c3b3fSnaddy 	size_t		 lbufsize = 0;
65bfd817adSflorian 	char		*param;
66bfd817adSflorian 	const char	*estr;
67bfd817adSflorian 	FILE		*fp;
68bfd817adSflorian 	struct cookie	*ck;
69bfd817adSflorian 
70bfd817adSflorian 	if (cookiefile == NULL)
71bfd817adSflorian 		return;
72bfd817adSflorian 
73bfd817adSflorian 	TAILQ_INIT(&jar);
74bfd817adSflorian 	fp = fopen(cookiefile, "r");
75bfd817adSflorian 	if (fp == NULL)
76bfd817adSflorian 		err(1, "cannot open cookie file %s", cookiefile);
77bfd817adSflorian 	date = time(NULL);
78*bb1c3b3fSnaddy 	while (getline(&lbuf, &lbufsize, fp) != -1) {
79bfd817adSflorian 		line = lbuf;
80*bb1c3b3fSnaddy 		line[strcspn(line, "\r\n")] = '\0';
81bfd817adSflorian 
82bfd817adSflorian 		line += strspn(line, " \t");
83bfd817adSflorian 		if ((*line == '#') || (*line == '\0')) {
84bfd817adSflorian 			continue;
85bfd817adSflorian 		}
86bfd817adSflorian 		field = DOMAIN;
87bfd817adSflorian 		ck = calloc(1, sizeof(*ck));
88bfd817adSflorian 		if (ck == NULL)
89bfd817adSflorian 			err(1, NULL);
90bfd817adSflorian 		while ((param = strsep(&line, "\t")) != NULL) {
91bfd817adSflorian 			switch (field) {
92bfd817adSflorian 			case DOMAIN:
93bfd817adSflorian 				if (*param == '.') {
94bfd817adSflorian 					if (asprintf(&ck->domain,
95bfd817adSflorian 					    "*%s", param) == -1)
96bfd817adSflorian 						err(1, NULL);
97bfd817adSflorian 				} else {
98bfd817adSflorian 					ck->domain = strdup(param);
99bfd817adSflorian 					if (ck->domain == NULL)
100bfd817adSflorian 						err(1, NULL);
101bfd817adSflorian 				}
102bfd817adSflorian 				break;
103bfd817adSflorian 			case TAILMATCH:
104bfd817adSflorian 				if (strcasecmp(param, "TRUE") == 0) {
105bfd817adSflorian 					ck->flags |= F_TAILMATCH;
106bfd817adSflorian 				} else if (strcasecmp(param, "FALSE") != 0) {
107bfd817adSflorian 					errx(1, "invalid cookie file");
108bfd817adSflorian 				}
109bfd817adSflorian 				break;
110bfd817adSflorian 			case PATH:
111bfd817adSflorian 				if (strcmp(param, "/") != 0) {
112bfd817adSflorian 					ck->flags |= F_MATCHPATH;
113bfd817adSflorian 					if (asprintf(&ck->path,
114bfd817adSflorian 					    "%s*", param) == -1)
115bfd817adSflorian 						err(1, NULL);
116bfd817adSflorian 				}
117bfd817adSflorian 				break;
118bfd817adSflorian 			case SECURE:
119bfd817adSflorian 				if (strcasecmp(param, "TRUE") == 0) {
120bfd817adSflorian 					ck->flags |= F_SECURE;
121bfd817adSflorian 				} else if (strcasecmp(param, "FALSE") != 0) {
122bfd817adSflorian 					errx(1, "invalid cookie file");
123bfd817adSflorian 				}
124bfd817adSflorian 				break;
125bfd817adSflorian 			case EXPIRES:
126bfd817adSflorian 				/*
127bfd817adSflorian 				 * rely on sizeof(time_t) being 4
128bfd817adSflorian 				 */
129bfd817adSflorian 				ck->expires = strtonum(param, 0,
130bfd817adSflorian 				    INT_MAX, &estr);
131bfd817adSflorian 				if (estr) {
132bfd817adSflorian 					if (errno == ERANGE)
133bfd817adSflorian 						ck->flags |= F_NOEXPIRY;
134bfd817adSflorian 					else
135bfd817adSflorian 						errx(1, "invalid cookie file");
136bfd817adSflorian 				}
137bfd817adSflorian 				break;
138bfd817adSflorian 			case NAME:
139bfd817adSflorian 				ck->key = strdup(param);
140bfd817adSflorian 				if (ck->key == NULL)
141bfd817adSflorian 					err(1, NULL);
142bfd817adSflorian 				break;
143bfd817adSflorian 			case VALUE:
144bfd817adSflorian 				ck->val = strdup(param);
145bfd817adSflorian 				if (ck->val == NULL)
146bfd817adSflorian 					err(1, NULL);
147bfd817adSflorian 				break;
148bfd817adSflorian 			case DONE:
149bfd817adSflorian 				errx(1, "invalid cookie file");
150bfd817adSflorian 				break;
151bfd817adSflorian 			}
152bfd817adSflorian 			field++;
153bfd817adSflorian 		}
154bfd817adSflorian 		if (field != DONE)
155bfd817adSflorian 			errx(1, "invalid cookie file");
156bfd817adSflorian 		if (ck->expires < date && !(ck->flags & F_NOEXPIRY)) {
157bfd817adSflorian 			free(ck->val);
158bfd817adSflorian 			free(ck->key);
159bfd817adSflorian 			free(ck->path);
160bfd817adSflorian 			free(ck->domain);
161bfd817adSflorian 			free(ck);
162bfd817adSflorian 		} else
163bfd817adSflorian 			TAILQ_INSERT_TAIL(&jar, ck, entry);
164bfd817adSflorian 	}
165bfd817adSflorian 	free(lbuf);
166bfd817adSflorian 	fclose(fp);
167bfd817adSflorian }
168bfd817adSflorian 
169bfd817adSflorian void
cookie_get(const char * domain,const char * path,int secure,char ** pstr)170bfd817adSflorian cookie_get(const char *domain, const char *path, int secure, char **pstr)
171bfd817adSflorian {
172bfd817adSflorian 	size_t		 len;
173bfd817adSflorian 	size_t		 headlen;
174bfd817adSflorian 	char		*head;
175bfd817adSflorian 	char		*str;
176bfd817adSflorian 	struct cookie	*ck;
177bfd817adSflorian 	struct cookiejar tempjar;
178bfd817adSflorian 
179bfd817adSflorian 	*pstr = NULL;
180bfd817adSflorian 
181bfd817adSflorian 	if (cookiefile == NULL)
182bfd817adSflorian 		return;
183bfd817adSflorian 
184bfd817adSflorian 	TAILQ_INIT(&tempjar);
185bfd817adSflorian 	len = strlen("Cookie\r\n");
186bfd817adSflorian 
187bfd817adSflorian 	TAILQ_FOREACH(ck, &jar, entry) {
188bfd817adSflorian 		if (fnmatch(ck->domain, domain, 0) == 0 &&
189bfd817adSflorian 		    (secure || !(ck->flags & F_SECURE))) {
190bfd817adSflorian 
191bfd817adSflorian 			if (ck->flags & F_MATCHPATH &&
192bfd817adSflorian 			    fnmatch(ck->path, path, 0) != 0)
193bfd817adSflorian 				continue;
194bfd817adSflorian 
195bfd817adSflorian 			len += strlen(ck->key) + strlen(ck->val) +
196bfd817adSflorian 			    strlen("; =");
197bfd817adSflorian 			TAILQ_INSERT_TAIL(&tempjar, ck, tempentry);
198bfd817adSflorian 		}
199bfd817adSflorian 	}
200bfd817adSflorian 	if (TAILQ_EMPTY(&tempjar))
201bfd817adSflorian 		return;
202bfd817adSflorian 	len += 1;
203bfd817adSflorian 	str = malloc(len);
204bfd817adSflorian 	if (str == NULL)
205bfd817adSflorian 		err(1, NULL);
206bfd817adSflorian 
207bfd817adSflorian 	(void)strlcpy(str, "Cookie:", len);
208bfd817adSflorian 	TAILQ_FOREACH(ck, &tempjar, tempentry) {
209bfd817adSflorian 		head = str + strlen(str);
210bfd817adSflorian 		headlen = len - strlen(str);
211bfd817adSflorian 
212bfd817adSflorian 		snprintf(head, headlen, "%s %s=%s",
213bfd817adSflorian 		    (ck == TAILQ_FIRST(&tempjar))? "" : ";", ck->key, ck->val);
214bfd817adSflorian 	}
215bfd817adSflorian 	if (strlcat(str, "\r\n", len) >= len)
216bfd817adSflorian 		errx(1, "cookie header truncated");
217bfd817adSflorian 	*pstr = str;
218bfd817adSflorian }
219bfd817adSflorian 
220bfd817adSflorian #endif /* !SMALL */
221bfd817adSflorian 
222