ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/auth.c
Revision: 9596
Committed: Fri Sep 4 16:51:08 2020 UTC (4 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 13621 byte(s)
Log Message:
- Cleanup ip address matching routine in various places

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2020 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file auth.c
23 * \brief Implementation of DNS and ident lookups.
24 * \version $Id$
25 */
26
27 /*
28 * Changes:
29 * July 6, 1999 - Rewrote most of the code here. When a client connects
30 * to the server and passes initial socket validation checks, it
31 * is owned by this module (auth) which returns it to the rest of the
32 * server when dns and auth queries are finished. Until the client is
33 * released, the server does not know it exists and does not process
34 * any messages from it.
35 * --Bleep Thomas Helvey <tomh@inxpress.net>
36 */
37
38 #include "stdinc.h"
39 #include "list.h"
40 #include "ircd_defs.h"
41 #include "fdlist.h"
42 #include "auth.h"
43 #include "conf.h"
44 #include "client.h"
45 #include "event.h"
46 #include "irc_string.h"
47 #include "ircd.h"
48 #include "packet.h"
49 #include "res.h"
50 #include "s_bsd.h"
51 #include "log.h"
52 #include "send.h"
53 #include "memory.h"
54 #include "misc.h"
55 #include "hostmask.h"
56
57
58 enum
59 {
60 REPORT_DO_DNS,
61 REPORT_FIN_DNS,
62 REPORT_FAIL_DNS,
63 REPORT_DO_ID,
64 REPORT_FIN_ID,
65 REPORT_FAIL_ID,
66 REPORT_IP_MISMATCH,
67 REPORT_HOST_TOOLONG,
68 REPORT_HOST_INVALID
69 };
70
71 static const char *const HeaderMessages[] =
72 {
73 [REPORT_DO_DNS] = ":*** Looking up your hostname",
74 [REPORT_FIN_DNS] = ":*** Found your hostname",
75 [REPORT_FAIL_DNS] = ":*** Couldn't look up your hostname",
76 [REPORT_DO_ID] = ":*** Checking Ident",
77 [REPORT_FIN_ID] = ":*** Got Ident response",
78 [REPORT_FAIL_ID] = ":*** No Ident response",
79 [REPORT_IP_MISMATCH] = ":*** Your forward and reverse DNS do not match, ignoring hostname",
80 [REPORT_HOST_TOOLONG] = ":*** Your hostname is too long, ignoring hostname",
81 [REPORT_HOST_INVALID] = ":*** Your hostname contains illegal characters, ignoring hostname"
82 };
83
84 #define auth_sendheader(c, i) sendto_one_notice((c), &me, "%s", HeaderMessages[(i)])
85
86
87
88 /*! \brief Allocate a new auth request.
89 * \param client The client being looked up.
90 * \return The newly allocated auth request.
91 */
92 static struct AuthRequest *
93 auth_make(struct Client *client)
94 {
95 struct AuthRequest *auth = xcalloc(sizeof(*auth));
96
97 auth->client = client;
98 auth->client->connection->auth = auth;
99
100 return auth;
101 }
102
103 /*! \brief Free memory
104 * \param auth The allocated auth request to cleanup.
105 */
106 static void
107 auth_free(struct AuthRequest *auth)
108 {
109 auth->client = NULL;
110 xfree(auth);
111 }
112
113 /*! \brief Release auth client from auth system. This adds the client into the
114 * local client lists so it can be read by the main io processing loop.
115 * \param auth Pointer to AuthRequest struct to release
116 */
117 static void
118 auth_release_client(struct AuthRequest *auth)
119 {
120 struct Client *client = auth->client;
121
122 assert(client);
123 assert(client->connection);
124
125 if (auth->ident_pending == true || auth->dns_pending == true)
126 return;
127
128 auth_free(auth);
129 client->connection->auth = NULL;
130
131 /*
132 * When a client has auth'ed, we want to start reading what it sends
133 * us. This is what read_packet() does.
134 * -- adrian
135 */
136 comm_setflush(client->connection->fd, 1, flood_recalc, client);
137
138 client->connection->last_ping = event_base->time.sec_monotonic;
139 client->connection->last_data = event_base->time.sec_monotonic;
140 client->connection->created_real = event_base->time.sec_real;
141 client->connection->created_monotonic = event_base->time.sec_monotonic;
142
143 AddFlag(client, FLAGS_FINISHED_AUTH);
144
145 read_packet(client->connection->fd, client);
146 }
147
148 /*! Checks if a hostname is valid and doesn't contain illegal characters
149 * \param hostname The string to verify
150 * \return 1 if it is valid, 0 if it isn't
151 */
152 static bool
153 auth_verify_hostname(const char *hostname)
154 {
155 const char *p = hostname;
156
157 assert(p);
158
159 if (EmptyString(p) || *p == '.' || *p == ':')
160 return false;
161
162 for (; *p; ++p)
163 if (!IsHostChar(*p))
164 return false;
165
166 return true;
167 }
168
169 /*! \brief Handle a complete DNS lookup. Send the client on its way to a connection
170 * completion, regardless of success or failure.
171 * \param vptr The pending struct AuthRequest.
172 * \param addr IP address being resolved.
173 * \param name Resolved name, or NULL if lookup failed.
174 * \param namelength String length of the resolved hostname pointed by 'name'
175 */
176 static void
177 auth_dns_callback(void *vptr, const struct irc_ssaddr *addr, const char *name, size_t namelength)
178 {
179 struct AuthRequest *const auth = vptr;
180
181 assert(auth->client);
182 assert(auth->client->connection);
183
184 auth->dns_pending = false;
185
186 if (namelength == 0)
187 auth_sendheader(auth->client, REPORT_FAIL_DNS);
188 else if (address_compare(addr, &auth->client->ip, true, false, 0) == false)
189 auth_sendheader(auth->client, REPORT_IP_MISMATCH);
190 else if (namelength > HOSTLEN)
191 auth_sendheader(auth->client, REPORT_HOST_TOOLONG);
192 else if (auth_verify_hostname(name) == false)
193 auth_sendheader(auth->client, REPORT_HOST_INVALID);
194 else
195 {
196 strlcpy(auth->client->host, name, sizeof(auth->client->host));
197 auth_sendheader(auth->client, REPORT_FIN_DNS);
198 }
199
200 auth_release_client(auth);
201 }
202
203 /*
204 * auth_error - handle auth send errors
205 */
206 static void
207 auth_error(struct AuthRequest *auth)
208 {
209 assert(auth);
210 assert(auth->fd);
211 assert(auth->client);
212 assert(auth->client->connection);
213
214 ++ServerStats.is_abad;
215
216 fd_close(auth->fd);
217 auth->fd = NULL;
218 auth->ident_pending = false;
219
220 auth_sendheader(auth->client, REPORT_FAIL_ID);
221
222 auth_release_client(auth);
223 }
224
225 /** Enum used to index ident reply fields in a human-readable way. */
226 enum IdentReplyFields
227 {
228 IDENT_PORT_NUMBERS,
229 IDENT_REPLY_TYPE,
230 IDENT_OS_TYPE,
231 IDENT_INFO,
232 USERID_TOKEN_COUNT
233 };
234
235 /** Parse an ident reply line and extract the userid from it.
236 * \param reply The ident reply line.
237 * \return The userid, or NULL on parse failure.
238 */
239 static const char *
240 auth_check_ident_reply(char *const reply)
241 {
242 char *token = NULL, *end = NULL;
243 char *vector[USERID_TOKEN_COUNT];
244 const unsigned int count = token_vector(reply, ':', vector, USERID_TOKEN_COUNT);
245
246 if (USERID_TOKEN_COUNT != count)
247 return NULL;
248
249 /*
250 * Second token is the reply type
251 */
252 token = vector[IDENT_REPLY_TYPE];
253
254 if (EmptyString(token))
255 return NULL;
256
257 while (IsSpace(*token))
258 ++token;
259
260 if (strncmp(token, "USERID", 6))
261 return NULL;
262
263 /*
264 * Third token is the os type
265 */
266 token = vector[IDENT_OS_TYPE];
267
268 if (EmptyString(token))
269 return NULL;
270
271 while (IsSpace(*token))
272 ++token;
273
274 /*
275 * Unless "OTHER" is specified as the operating system type, the server
276 * is expected to return the "normal" user identification of the owner
277 * of this connection. "Normal" in this context may be taken to mean a
278 * string of characters which uniquely identifies the connection owner
279 * such as a user identifier assigned by the system administrator and
280 * used by such user as a mail identifier, or as the "user" part of a
281 * user/password pair used to gain access to system resources. When an
282 * operating system is specified (e.g., anything but "OTHER"), the user
283 * identifier is expected to be in a more or less immediately useful
284 * form - e.g., something that could be used as an argument to "finger"
285 * or as a mail address.
286 */
287 if (strncmp(token, "OTHER", 5) == 0)
288 return NULL;
289
290 /*
291 * Fourth token is the username
292 */
293 token = vector[IDENT_INFO];
294
295 if (EmptyString(token))
296 return NULL;
297
298 while (IsSpace(*token))
299 ++token;
300
301 while (*token == '~' || *token == '^')
302 ++token;
303
304 /*
305 * Look for the end of the username, terminators are '\0, @, <SPACE>, :'
306 */
307 for (end = token; *end; ++end)
308 if (IsSpace(*end) || '@' == *end || ':' == *end)
309 break;
310 *end = '\0';
311
312 return token;
313 }
314
315 /*! \brief Read the reply (if any) from the ident server we connected to. We
316 * only give it one shot, if the reply isn't good the first time fail
317 * the authentication entirely. --Bleep
318 * \param F The socket/fd to read from.
319 * \param data The request to read.
320 */
321 static void
322 auth_read_reply(fde_t *F, void *data)
323 {
324 struct AuthRequest *const auth = data;
325 const char *username = NULL;
326 ssize_t len = 0;
327 char buf[RFC1413_BUFSIZ + 1];
328
329 assert(auth->fd == F);
330 assert(auth->client);
331 assert(auth->client->connection);
332
333 if (F->read_handler == NULL && (len = recv(auth->fd->fd, buf, sizeof(buf) - 1, 0)) > 0)
334 {
335 buf[len] = '\0';
336 username = auth_check_ident_reply(buf);
337 }
338
339 fd_close(auth->fd);
340 auth->fd = NULL;
341 auth->ident_pending = false;
342
343 if (EmptyString(username))
344 {
345 auth_sendheader(auth->client, REPORT_FAIL_ID);
346 ++ServerStats.is_abad;
347 }
348 else
349 {
350 strlcpy(auth->client->username, username, sizeof(auth->client->username));
351 auth_sendheader(auth->client, REPORT_FIN_ID);
352 ++ServerStats.is_asuc;
353 AddFlag(auth->client, FLAGS_GOTID);
354 }
355
356 auth_release_client(auth);
357 }
358
359 /*
360 * auth_connect_callback() - deal with the result of comm_connect_tcp()
361 *
362 * If the connection failed, we simply close the auth fd and report
363 * a failure. If the connection suceeded send the ident server a query
364 * giving "theirport , ourport". The write is only attempted *once* so
365 * it is deemed to be a fail if the entire write doesn't write all the
366 * data given. This shouldnt be a problem since the socket should have
367 * a write buffer far greater than this message to store it in should
368 * problems arise. -avalon
369 */
370 static void
371 auth_connect_callback(fde_t *F, int error, void *data)
372 {
373 struct AuthRequest *const auth = data;
374 struct irc_ssaddr us;
375 struct irc_ssaddr them;
376 char authbuf[16];
377 ssize_t len = 0;
378 socklen_t ulen = sizeof(struct irc_ssaddr);
379 socklen_t tlen = sizeof(struct irc_ssaddr);
380 uint16_t uport, tport;
381 struct sockaddr_in6 *v6;
382
383 assert(auth->fd == F);
384 assert(auth->client);
385 assert(auth->client->connection);
386
387 if (error != COMM_OK)
388 {
389 auth_error(auth);
390 return;
391 }
392
393 if (getsockname(auth->client->connection->fd->fd, (struct sockaddr *)&us, &ulen) ||
394 getpeername(auth->client->connection->fd->fd, (struct sockaddr *)&them, &tlen))
395 {
396 report_error(L_ALL, "auth get{sock,peer}name error %s:%s",
397 client_get_name(auth->client, SHOW_IP), errno);
398 auth_error(auth);
399 return;
400 }
401
402 v6 = (struct sockaddr_in6 *)&us;
403 uport = ntohs(v6->sin6_port);
404 v6 = (struct sockaddr_in6 *)&them;
405 tport = ntohs(v6->sin6_port);
406
407 len = snprintf(authbuf, sizeof(authbuf), "%u, %u\r\n", tport, uport);
408
409 if (send(F->fd, authbuf, len, 0) != len)
410 {
411 auth_error(auth);
412 return;
413 }
414
415 comm_setselect(F, COMM_SELECT_READ, auth_read_reply, auth, 4);
416 }
417
418 /*! \brief Flag the client to show an attempt to contact the ident server on
419 * the client's host. Should the connect or any later phase of the
420 * identifying process fail, it is aborted and the user is given a
421 * username of "unknown".
422 * \param auth The request for which to start the ident lookup.
423 */
424 static void
425 auth_start_query(struct AuthRequest *auth)
426 {
427 struct irc_ssaddr localaddr;
428 socklen_t locallen = sizeof(struct irc_ssaddr);
429 struct sockaddr_in6 *v6;
430
431 assert(auth->client);
432 assert(auth->client->connection);
433
434 /* Open a socket of the same type as the client socket */
435 int fd = comm_socket(auth->client->ip.ss.ss_family, SOCK_STREAM, 0);
436 if (fd == -1)
437 {
438 report_error(L_ALL, "creating auth stream socket %s:%s",
439 client_get_name(auth->client, SHOW_IP), errno);
440 ++ServerStats.is_abad;
441 return;
442 }
443
444 auth->fd = fd_open(fd, true, "ident");
445 auth->ident_pending = true;
446
447 auth_sendheader(auth->client, REPORT_DO_ID);
448
449 /*
450 * Get the local address of the client and bind to that to
451 * make the auth request.
452 */
453 memset(&localaddr, 0, locallen);
454 getsockname(auth->client->connection->fd->fd, (struct sockaddr *)&localaddr, &locallen);
455
456 remove_ipv6_mapping(&localaddr);
457 v6 = (struct sockaddr_in6 *)&localaddr;
458 v6->sin6_port = htons(0);
459
460 comm_connect_tcp(auth->fd, &auth->client->ip, RFC1413_PORT, &localaddr,
461 auth_connect_callback, auth, 4);
462 }
463
464 /*
465 * auth_start
466 *
467 * inputs - pointer to client to auth
468 * output - NONE
469 * side effects - starts auth (identd) and dns queries for a client
470 */
471 void
472 auth_start(struct Client *client)
473 {
474 struct AuthRequest *auth = auth_make(client);
475
476 assert(client);
477 assert(client->connection);
478
479 auth_sendheader(client, REPORT_DO_DNS);
480
481 auth->dns_pending = true;
482
483 if (ConfigGeneral.disable_auth == 0)
484 auth_start_query(auth);
485
486 gethost_byaddr(auth_dns_callback, auth, &client->ip);
487 }
488
489 /*
490 * auth_delete()
491 */
492 void
493 auth_delete(struct AuthRequest *auth)
494 {
495 assert(auth->client);
496 assert(auth->client->connection);
497
498 if (auth->fd)
499 {
500 fd_close(auth->fd);
501 auth->fd = NULL;
502 }
503
504 auth->ident_pending = false;
505
506 delete_resolver_queries(auth);
507 auth->dns_pending = false;
508
509 auth_free(auth);
510 }

Properties

Name Value
svn:eol-style native
svn:keywords Id