Commit | Line | Data |
---|---|---|
9dcdc856 MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Discover the owner of a connection | |
4 | * | |
5 | * (c) 2012 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of Yet Another Ident Daemon (YAID). | |
11 | * | |
12 | * YAID is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * YAID is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with YAID; if not, write to the Free Software Foundation, | |
24 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
25 | */ | |
26 | ||
27 | /*----- Header files ------------------------------------------------------*/ | |
28 | ||
9da480be | 29 | #include "yaid.h" |
9dcdc856 MW |
30 | |
31 | /*----- Static variables --------------------------------------------------*/ | |
32 | ||
9da480be | 33 | const char *const errtok[] = { |
9dcdc856 MW |
34 | #define DEFTOK(err, tok) tok, |
35 | ERROR(DEFTOK) | |
36 | #undef DEFTOK | |
37 | }; | |
38 | ||
39 | static int parseaddr4(char **pp, union addr *a) | |
9da480be | 40 | { a->ipv4.s_addr = strtoul(*pp, pp, 16); return (0); } |
9dcdc856 MW |
41 | |
42 | static int addreq4(const union addr *a, const union addr *aa) | |
43 | { return a->ipv4.s_addr == aa->ipv4.s_addr; } | |
44 | ||
9da480be MW |
45 | static int parseaddr6(char **pp, union addr *a) |
46 | { | |
47 | int i, j; | |
48 | unsigned long y; | |
49 | char *p = *pp; | |
50 | unsigned x; | |
51 | ||
52 | for (i = 0; i < 4; i++) { | |
53 | y = 0; | |
54 | for (j = 0; j < 8; j++) { | |
55 | if ('0' <= *p && *p <= '9') x = *p - '0'; | |
56 | else if ('a' <= *p && *p <= 'f') x = *p - 'a'+ 10; | |
57 | else if ('A' <= *p && *p <= 'F') x = *p - 'A'+ 10; | |
58 | else return (-1); | |
59 | y = (y << 4) | x; | |
60 | p++; | |
61 | } | |
62 | a->ipv6.s6_addr32[i] = y; | |
63 | } | |
64 | *pp = p; | |
65 | return (0); | |
66 | } | |
67 | ||
68 | static int addreq6(const union addr *a, const union addr *b) | |
69 | { return !memcmp(a->ipv6.s6_addr, b->ipv6.s6_addr, 16); } | |
70 | ||
9dcdc856 MW |
71 | static const struct addrfamily { |
72 | int af; | |
73 | const char *procfile; | |
74 | int (*parseaddr)(char **pp, union addr *a); | |
75 | int (*addreq)(const union addr *a, const union addr *aa); | |
76 | } addrfamilytab[] = { | |
77 | { AF_INET, "/proc/net/tcp", parseaddr4, addreq4 }, | |
9da480be | 78 | { AF_INET6, "/proc/net/tcp6", parseaddr6, addreq6 }, |
9dcdc856 MW |
79 | { -1 } |
80 | }; | |
81 | ||
82 | /*----- Main code ---------------------------------------------------------*/ | |
83 | ||
9da480be MW |
84 | static int sockeq(const struct addrfamily *af, |
85 | const struct socket *sa, const struct socket *sb) | |
86 | { return (af->addreq(&sa->addr, &sb->addr) && sa->port == sb->port); } | |
87 | ||
88 | int get_default_gw(int af, union addr *a) | |
9dcdc856 | 89 | { |
9da480be MW |
90 | int fd; |
91 | char buf[32768]; | |
92 | struct nlmsghdr *nlmsg; | |
93 | struct rtgenmsg *rtgen; | |
94 | const struct rtattr *rta; | |
95 | const struct rtmsg *rtm; | |
96 | ssize_t n, nn; | |
97 | int rc = 0; | |
98 | static unsigned long seq = 0x48b4aec4; | |
99 | ||
100 | if ((fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) | |
101 | die(1, "failed to create netlink socket: %s", strerror(errno)); | |
102 | ||
103 | nlmsg = (struct nlmsghdr *)buf; | |
104 | assert(NLMSG_SPACE(sizeof(*rtgen)) < sizeof(buf)); | |
105 | nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(*rtgen)); | |
106 | nlmsg->nlmsg_type = RTM_GETROUTE; | |
107 | nlmsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; | |
108 | nlmsg->nlmsg_seq = ++seq; | |
109 | nlmsg->nlmsg_pid = 0; | |
110 | ||
111 | rtgen = (struct rtgenmsg *)NLMSG_DATA(nlmsg); | |
112 | rtgen->rtgen_family = af; | |
113 | ||
114 | if (write(fd, nlmsg, nlmsg->nlmsg_len) < 0) | |
115 | die(1, "failed to send RTM_GETROUTE request: %s", strerror(errno)); | |
9dcdc856 | 116 | |
9da480be MW |
117 | for (;;) { |
118 | if ((n = read(fd, buf, sizeof(buf))) < 0) | |
119 | die(1, "failed to read RTM_GETROUTE response: %s", strerror(errno)); | |
120 | nlmsg = (struct nlmsghdr *)buf; | |
121 | if (nlmsg->nlmsg_seq != seq) continue; | |
122 | assert(nlmsg->nlmsg_flags & NLM_F_MULTI); | |
123 | ||
124 | for (; NLMSG_OK(nlmsg, n); nlmsg = NLMSG_NEXT(nlmsg, n)) { | |
125 | if (nlmsg->nlmsg_type == NLMSG_DONE) goto done; | |
126 | if (nlmsg->nlmsg_type != RTM_NEWROUTE) continue; | |
127 | rtm = (const struct rtmsg *)NLMSG_DATA(nlmsg); | |
128 | ||
129 | if (rtm->rtm_family != af || | |
130 | rtm->rtm_dst_len > 0 || | |
131 | rtm->rtm_src_len > 0 || | |
132 | rtm->rtm_type != RTN_UNICAST || | |
133 | rtm->rtm_scope != RT_SCOPE_UNIVERSE || | |
134 | rtm->rtm_tos != 0) | |
135 | continue; | |
9dcdc856 | 136 | |
9da480be MW |
137 | for (rta = RTM_RTA(rtm), nn = RTM_PAYLOAD(nlmsg); |
138 | RTA_OK(rta, nn); rta = RTA_NEXT(rta, nn)) { | |
139 | if (rta->rta_type == RTA_GATEWAY) { | |
140 | assert(RTA_PAYLOAD(rta) <= sizeof(*a)); | |
141 | memcpy(a, RTA_DATA(rta), RTA_PAYLOAD(rta)); | |
142 | rc = 1; | |
143 | } | |
144 | } | |
145 | } | |
146 | } | |
9dcdc856 | 147 | |
9da480be MW |
148 | done: |
149 | close(fd); | |
150 | return (rc); | |
9dcdc856 MW |
151 | } |
152 | ||
9da480be | 153 | void identify(struct query *q) |
9dcdc856 MW |
154 | { |
155 | const struct addrfamily *af; | |
156 | FILE *fp = 0; | |
157 | dstr d = DSTR_INIT; | |
158 | char *p, *pp; | |
159 | struct socket s[4]; | |
160 | int i; | |
9da480be | 161 | int gwp = 0; |
9dcdc856 MW |
162 | unsigned fl; |
163 | #define F_SADDR 1u | |
164 | #define F_SPORT 2u | |
165 | #define F_DADDR 4u | |
166 | #define F_DPORT 8u | |
167 | #define F_ALL (F_SADDR | F_SPORT | F_DADDR | F_DPORT) | |
168 | #define F_ESTAB 16u | |
169 | uid_t uid; | |
170 | enum { LOC, REM, ST, UID, NFIELD }; | |
171 | int f, ff[NFIELD]; | |
172 | ||
173 | for (af = addrfamilytab; af->af >= 0; af++) | |
174 | if (af->af == q->af) goto found_af; | |
175 | logmsg(q, LOG_ERR, "unexpected address family `%d'", q->af); | |
176 | goto err_unk; | |
177 | found_af:; | |
178 | ||
9da480be MW |
179 | if (get_default_gw(q->af, &s[0].addr) && |
180 | af->addreq(&s[0].addr, &q->s[R].addr)) | |
181 | gwp = 1; | |
182 | ||
9dcdc856 MW |
183 | if ((fp = fopen(af->procfile, "r")) == 0) { |
184 | logmsg(q, LOG_ERR, "failed to open `%s' for reading: %s", | |
185 | af->procfile, strerror(errno)); | |
186 | goto err_unk; | |
187 | } | |
188 | ||
189 | #define NEXTFIELD do { \ | |
190 | for (p = pp; isspace((unsigned char)*p); p++); \ | |
191 | for (pp = p; *pp && !isspace((unsigned char)*pp); pp++); \ | |
192 | if (*pp) *pp++ = 0; \ | |
193 | } while (0) | |
194 | ||
195 | if (dstr_putline(&d, fp) == EOF) { | |
196 | logmsg(q, LOG_ERR, "failed to read header line from `%s': %s", | |
197 | af->procfile, ferror(fp) ? strerror(errno) : "unexpected EOF"); | |
198 | goto err_unk; | |
199 | } | |
200 | ||
201 | for (i = 0; i < NFIELD; i++) ff[i] = -1; | |
202 | pp = d.buf; | |
203 | for (f = 0;; f++) { | |
204 | NEXTFIELD; if (!*p) break; | |
205 | if (strcmp(p, "local_address") == 0) | |
206 | ff[LOC] = f; | |
207 | else if (strcmp(p, "rem_address") == 0 || | |
208 | strcmp(p, "remote_address") == 0) | |
209 | ff[REM] = f; | |
210 | else if (strcmp(p, "uid") == 0) | |
211 | ff[UID] = f; | |
212 | else if (strcmp(p, "st") == 0) | |
213 | ff[ST] = f; | |
214 | else if (strcmp(p, "rx_queue") == 0 || | |
215 | strcmp(p, "tm->when") == 0) | |
216 | f--; | |
217 | } | |
218 | for (i = 0; i < NFIELD; i++) { | |
219 | if (ff[i] < 0) { | |
220 | logmsg(q, LOG_ERR, "failed to find required fields in `%s'", | |
221 | af->procfile); | |
222 | goto err_unk; | |
223 | } | |
224 | } | |
225 | ||
226 | for (;;) { | |
227 | DRESET(&d); | |
228 | if (dstr_putline(&d, fp) == EOF) break; | |
229 | pp = d.buf; | |
230 | uid = -1; | |
231 | for (f = 0;; f++) { | |
232 | NEXTFIELD; if (!*p) break; | |
233 | if (f == ff[LOC]) { i = L; goto compare; } | |
234 | else if (f == ff[REM]) { i = R; goto compare; } | |
235 | else if (f == ff[UID]) uid = atoi(p); | |
236 | else if (f == ff[ST]) { | |
237 | if (strtol(p, 0, 16) != 1) goto next_row; | |
238 | } | |
239 | continue; | |
240 | ||
241 | compare: | |
242 | if (af->parseaddr(&p, &s[0].addr)) goto next_row; | |
243 | if (*p != ':') break; p++; | |
244 | s[0].port = strtoul(p, 0, 16); | |
9da480be MW |
245 | if (!sockeq(af, &q->s[i], &s[0]) && |
246 | (i != R || !gwp || q->s[R].port != s[0].port)) | |
247 | goto next_row; | |
9dcdc856 MW |
248 | } |
249 | if (uid != -1) { | |
9da480be MW |
250 | q->resp = R_UID; |
251 | q->u.uid = uid; | |
9dcdc856 MW |
252 | goto done; |
253 | } | |
254 | next_row:; | |
255 | } | |
256 | ||
257 | if (ferror(fp)) { | |
258 | logmsg(q, LOG_ERR, "failed to read connection table: %s", | |
259 | strerror(errno)); | |
260 | goto err_unk; | |
261 | } | |
262 | ||
263 | if (q->af == AF_INET) { | |
264 | fclose(fp); | |
265 | if ((fp = fopen("/proc/net/ip_conntrack", "r")) == 0) { | |
266 | if (errno == ENOENT) | |
267 | goto err_nouser; | |
268 | else { | |
269 | logmsg(q, LOG_ERR, | |
270 | "failed to open `/proc/net/ip_conntrack' for reading: %s", | |
271 | strerror(errno)); | |
272 | goto err_unk; | |
273 | } | |
274 | } | |
275 | ||
276 | for (;;) { | |
277 | DRESET(&d); | |
278 | if (dstr_putline(&d, fp) == EOF) break; | |
279 | pp = d.buf; | |
280 | NEXTFIELD; if (!*p) break; | |
281 | if (strcmp(p, "tcp") != 0) continue; | |
282 | i = 0; | |
283 | fl = 0; | |
284 | for (;;) { | |
285 | NEXTFIELD; if (!*p) break; | |
286 | if (strcmp(p, "ESTABLISHED") == 0) | |
287 | fl |= F_ESTAB; | |
288 | else if (strncmp(p, "src=", 4) == 0) { | |
289 | inet_pton(AF_INET, p + 4, &s[i].addr); | |
290 | fl |= F_SADDR; | |
291 | } else if (strncmp(p, "dst=", 4) == 0) { | |
292 | inet_pton(AF_INET, p + 4, &s[i + 1].addr); | |
293 | fl |= F_DADDR; | |
294 | } else if (strncmp(p, "sport=", 6) == 0) { | |
295 | s[i].port = atoi(p + 6); | |
296 | fl |= F_SPORT; | |
297 | } else if (strncmp(p, "dport=", 6) == 0) { | |
298 | s[i + 1].port = atoi(p + 6); | |
299 | fl |= F_DPORT; | |
300 | } | |
301 | if ((fl & F_ALL) == F_ALL) { | |
302 | fl &= ~F_ALL; | |
303 | if (i < 4) i += 2; | |
304 | else break; | |
305 | } | |
306 | } | |
307 | ||
308 | #ifdef notdef | |
309 | { | |
310 | dstr dd = DSTR_INIT; | |
311 | dstr_putf(&dd, "%sestab ", (fl & F_ESTAB) ? " " : "!"); | |
312 | dputsock(&dd, af->af, &s[0]); | |
313 | dstr_puts(&dd, "<->"); | |
314 | dputsock(&dd, af->af, &s[1]); | |
315 | dstr_puts(&dd, " | "); | |
316 | dputsock(&dd, af->af, &s[2]); | |
317 | dstr_puts(&dd, "<->"); | |
318 | dputsock(&dd, af->af, &s[3]); | |
319 | printf("parsed: %s\n", dd.buf); | |
320 | dstr_destroy(&dd); | |
321 | } | |
322 | #endif | |
323 | ||
324 | if (!(fl & F_ESTAB)) continue; | |
325 | ||
326 | for (i = 0; i < 4; i++) | |
327 | if (sockeq(af, &s[i], &q->s[L])) goto found_local; | |
328 | continue; | |
329 | putchar('.'); | |
330 | found_local: | |
331 | if (!sockeq(af, &s[i^1], &s[i^2]) || | |
332 | !sockeq(af, &s[i^1], &q->s[R])) | |
333 | continue; | |
9da480be MW |
334 | q->resp = R_NAT; |
335 | q->u.nat = s[i^3]; | |
9dcdc856 MW |
336 | goto done; |
337 | } | |
338 | ||
339 | if (ferror(fp)) { | |
340 | logmsg(q, LOG_ERR, "failed to read `/proc/net/ip_conntrack': %s", | |
341 | strerror(errno)); | |
342 | goto err_unk; | |
343 | } | |
9da480be | 344 | logmsg(q, LOG_ERR, "connection not found"); |
9dcdc856 MW |
345 | } |
346 | ||
347 | #undef NEXTFIELD | |
348 | ||
349 | err_nouser: | |
9da480be MW |
350 | q->resp = R_ERROR; |
351 | q->u.error = E_NOUSER; | |
9dcdc856 MW |
352 | goto done; |
353 | err_unk: | |
9da480be MW |
354 | q->resp = R_ERROR; |
355 | q->u.error = E_UNKNOWN; | |
9dcdc856 MW |
356 | done: |
357 | dstr_destroy(&d); | |
358 | } | |
359 | ||
360 | /*----- That's all, folks -------------------------------------------------*/ |