xref: /openbsd/usr.bin/ftp/cookie.c (revision bb1c3b3f)
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 
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
cookie_load(void)58 cookie_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 
169 void
cookie_get(const char * domain,const char * path,int secure,char ** pstr)170 cookie_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