xref: /openbsd/usr.bin/ftp/cookie.c (revision 3cab2bb3)
1 /*	$OpenBSD: cookie.c,v 1.9 2019/05/16 12:44:17 florian 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 
34 struct 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 };
48 TAILQ_HEAD(cookiejar, cookie);
49 
50 typedef enum {
51 	DOMAIN = 0, TAILMATCH = 1, PATH = 2, SECURE = 3,
52 	EXPIRES = 4, NAME = 5, VALUE = 6, DONE = 7
53 } field_t;
54 
55 static struct cookiejar jar;
56 
57 void
58 cookie_load(void)
59 {
60 	field_t		 field;
61 	size_t		 len;
62 	time_t		 date;
63 	char		*line;
64 	char		*lbuf;
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 	lbuf = NULL;
79 	while ((line = fgetln(fp, &len)) != NULL) {
80 		if (line[len - 1] == '\n') {
81 			line[len - 1] = '\0';
82 			--len;
83 		} else {
84 			if ((lbuf = malloc(len + 1)) == NULL)
85 				err(1, NULL);
86 			memcpy(lbuf, line, len);
87 			lbuf[len] = '\0';
88 			line = lbuf;
89 		}
90 		line[strcspn(line, "\r")] = '\0';
91 
92 		line += strspn(line, " \t");
93 		if ((*line == '#') || (*line == '\0')) {
94 			continue;
95 		}
96 		field = DOMAIN;
97 		ck = calloc(1, sizeof(*ck));
98 		if (ck == NULL)
99 			err(1, NULL);
100 		while ((param = strsep(&line, "\t")) != NULL) {
101 			switch (field) {
102 			case DOMAIN:
103 				if (*param == '.') {
104 					if (asprintf(&ck->domain,
105 					    "*%s", param) == -1)
106 						err(1, NULL);
107 				} else {
108 					ck->domain = strdup(param);
109 					if (ck->domain == NULL)
110 						err(1, NULL);
111 				}
112 				break;
113 			case TAILMATCH:
114 				if (strcasecmp(param, "TRUE") == 0) {
115 					ck->flags |= F_TAILMATCH;
116 				} else if (strcasecmp(param, "FALSE") != 0) {
117 					errx(1, "invalid cookie file");
118 				}
119 				break;
120 			case PATH:
121 				if (strcmp(param, "/") != 0) {
122 					ck->flags |= F_MATCHPATH;
123 					if (asprintf(&ck->path,
124 					    "%s*", param) == -1)
125 						err(1, NULL);
126 				}
127 				break;
128 			case SECURE:
129 				if (strcasecmp(param, "TRUE") == 0) {
130 					ck->flags |= F_SECURE;
131 				} else if (strcasecmp(param, "FALSE") != 0) {
132 					errx(1, "invalid cookie file");
133 				}
134 				break;
135 			case EXPIRES:
136 				/*
137 				 * rely on sizeof(time_t) being 4
138 				 */
139 				ck->expires = strtonum(param, 0,
140 				    INT_MAX, &estr);
141 				if (estr) {
142 					if (errno == ERANGE)
143 						ck->flags |= F_NOEXPIRY;
144 					else
145 						errx(1, "invalid cookie file");
146 				}
147 				break;
148 			case NAME:
149 				ck->key = strdup(param);
150 				if (ck->key == NULL)
151 					err(1, NULL);
152 				break;
153 			case VALUE:
154 				ck->val = strdup(param);
155 				if (ck->val == NULL)
156 					err(1, NULL);
157 				break;
158 			case DONE:
159 				errx(1, "invalid cookie file");
160 				break;
161 			}
162 			field++;
163 		}
164 		if (field != DONE)
165 			errx(1, "invalid cookie file");
166 		if (ck->expires < date && !(ck->flags & F_NOEXPIRY)) {
167 			free(ck->val);
168 			free(ck->key);
169 			free(ck->path);
170 			free(ck->domain);
171 			free(ck);
172 		} else
173 			TAILQ_INSERT_TAIL(&jar, ck, entry);
174 	}
175 	free(lbuf);
176 	fclose(fp);
177 }
178 
179 void
180 cookie_get(const char *domain, const char *path, int secure, char **pstr)
181 {
182 	size_t		 len;
183 	size_t		 headlen;
184 	char		*head;
185 	char		*str;
186 	struct cookie	*ck;
187 	struct cookiejar tempjar;
188 
189 	*pstr = NULL;
190 
191 	if (cookiefile == NULL)
192 		return;
193 
194 	TAILQ_INIT(&tempjar);
195 	len = strlen("Cookie\r\n");
196 
197 	TAILQ_FOREACH(ck, &jar, entry) {
198 		if (fnmatch(ck->domain, domain, 0) == 0 &&
199 		    (secure || !(ck->flags & F_SECURE))) {
200 
201 			if (ck->flags & F_MATCHPATH &&
202 			    fnmatch(ck->path, path, 0) != 0)
203 				continue;
204 
205 			len += strlen(ck->key) + strlen(ck->val) +
206 			    strlen("; =");
207 			TAILQ_INSERT_TAIL(&tempjar, ck, tempentry);
208 		}
209 	}
210 	if (TAILQ_EMPTY(&tempjar))
211 		return;
212 	len += 1;
213 	str = malloc(len);
214 	if (str == NULL)
215 		err(1, NULL);
216 
217 	(void)strlcpy(str, "Cookie:", len);
218 	TAILQ_FOREACH(ck, &tempjar, tempentry) {
219 		head = str + strlen(str);
220 		headlen = len - strlen(str);
221 
222 		snprintf(head, headlen, "%s %s=%s",
223 		    (ck == TAILQ_FIRST(&tempjar))? "" : ";", ck->key, ck->val);
224 	}
225 	if (strlcat(str, "\r\n", len) >= len)
226 		errx(1, "cookie header truncated");
227 	*pstr = str;
228 }
229 
230 #endif /* !SMALL */
231 
232