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