diff options
Diffstat (limited to 'ircd/s_auth.c')
-rw-r--r-- | ircd/s_auth.c | 804 |
1 files changed, 804 insertions, 0 deletions
diff --git a/ircd/s_auth.c b/ircd/s_auth.c new file mode 100644 index 0000000..89d19ed --- /dev/null +++ b/ircd/s_auth.c @@ -0,0 +1,804 @@ +/************************************************************************ + * IRC - Internet Relay Chat, ircd/s_auth.c + * Copyright (C) 1992 Darren Reed + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef lint +static char rcsid[] = "@(#)$Id: s_auth.c,v 1.43 1999/07/02 16:38:21 kalt Exp $"; +#endif + +#include "os.h" +#include "s_defines.h" +#define S_AUTH_C +#include "s_externs.h" +#undef S_AUTH_C + +/* + * set_clean_username + * + * As non OTHER type of usernames retrieved via ident lookup are forced as + * usernames for the user, one has to be careful not to allow potentially + * damaging characters in the username. + * This procedure fills up cptr->username based on cptr->auth + * Characters disallowed: + * leading : for obvious reasons + * spaces for obvious reasons + * @ because of how get_sockhost() is implemented, + * and because it's used from attached_Iline() + * [ /trace parsing is impossible + */ +static void +set_clean_username(cptr) +aClient *cptr; +{ + int i = 0, dirty = 0; + char *s; + + if (cptr->auth == NULL) + return; + s = cptr->auth; + if (index(cptr->auth, '[') || index(cptr->auth, '@') || + strlen(cptr->auth) > USERLEN) + dirty = 1; + else if (cptr->auth[0] == ':') + { + dirty = 1; + s += 1; + } + else + { + char *r = cptr->auth; + + while (*r) + if (isspace(*(r++))) + break; + if (*r) + dirty = 1; + } + if (dirty) + cptr->username[i++] = '-'; + while (i < USERLEN && *s) + { + if (*s != '@' && *s != '[' && !isspace(*s)) + cptr->username[i++] = *s; + s += 1; + } + cptr->username[i] = '\0'; + if (!strcmp(cptr->username, cptr->auth)) + { + MyFree(cptr->auth); + cptr->auth = cptr->username; + } + else + { + istat.is_authmem += sizeof(cptr->auth); + istat.is_auth += 1; + } +} + +#if defined(USE_IAUTH) + +u_char iauth_options = 0; +u_int iauth_spawn = 0; +char *iauth_version = NULL; + +static aExtCf *iauth_conf = NULL; +static aExtData *iauth_stats = NULL; + +/* + * sendto_iauth + * + * Send the buffer to the authentication slave process. + * Return 0 if everything went well, -1 otherwise. + */ +#if ! USE_STDARG +int +sendto_iauth(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) +char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10; +#else +int +vsendto_iauth(char *pattern, va_list va) +#endif +{ + static char abuf[BUFSIZ]; + +#if ! USE_STDARG + sprintf(abuf, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); +#else + vsprintf(abuf, pattern, va); +#endif + strcat(abuf, "\n"); + + if (adfd < 0) + return -1; + if (write(adfd, abuf, strlen(abuf)) != strlen(abuf)) + { + sendto_flag(SCH_AUTH, "Aiiie! lost slave authentication process"); + close(adfd); + adfd = -1; + start_iauth(0); + return -1; + } + return 0; +} + +# if USE_STDARG +int +sendto_iauth(char *pattern, ...) +{ + int i; + + va_list va; + va_start(va, pattern); + i = vsendto_iauth(pattern, va); + va_end(va); + return i; +} +# endif + +/* + * read_iauth + * + * read and process data from the authentication slave process. + */ +void +read_iauth() +{ + static char obuf[READBUF_SIZE+1], last = '?'; + static int olen = 0, ia_dbg = 0; + char buf[READBUF_SIZE+1], *start, *end, tbuf[BUFSIZ]; + aClient *cptr; + int i; + + if (adfd == -1) + { + olen = 0; + return; + } + while (1) + { + if (olen) + bcopy(obuf, buf, olen); + if ((i = recv(adfd, buf+olen, READBUF_SIZE-olen, 0)) <= 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK) + { + sendto_flag(SCH_AUTH, "Aiiie! lost slave authentication process (errno = %d)", errno); + close(adfd); + adfd = -1; + start_iauth(0); + } + break; + } + olen += i; + buf[olen] = '\0'; + start = buf; + while (end = index(start, '\n')) + { + *end++ = '\0'; + last = *start; + if (*start == '>') + { + sendto_flag(SCH_AUTH, "%s", start+1); + start = end; + continue; + } + if (*start == 'G') + { + ia_dbg = atoi(start+2); + if (ia_dbg) + sendto_flag(SCH_AUTH,"ia_dbg = %d",ia_dbg); + start = end; + continue; + } + if (*start == 'O') /* options */ + { + iauth_options = 0; + if (strchr(start+2, 'A')) + iauth_options |= XOPT_EARLYPARSE; + if (strchr(start+2, 'R')) + iauth_options |= XOPT_REQUIRED; + if (strchr(start+2, 'T')) + iauth_options |= XOPT_NOTIMEOUT; + if (strchr(start+2, 'W')) + iauth_options |= XOPT_EXTWAIT; + if (iauth_options) + sendto_flag(SCH_AUTH, "iauth options: %x", + iauth_options); + start = end; + continue; + } + if (*start == 'V') /* version */ + { + if (iauth_version) + MyFree(iauth_version); + iauth_version = mystrdup(start+2); + sendto_flag(SCH_AUTH, "iauth version %s running.", + iauth_version); + start = end; + continue; + } + if (*start == 'a') + { + aExtCf *ectmp; + + while (ectmp = iauth_conf) + { + iauth_conf = iauth_conf->next; + MyFree(ectmp->line); + MyFree(ectmp); + } + /* little lie.. ;) */ + sendto_flag(SCH_AUTH, "New iauth configuration."); + start = end; + continue; + } + if (*start == 'A') + { + aExtCf **ectmp = &iauth_conf; + + while (*ectmp) + ectmp = &((*ectmp)->next); + *ectmp = (aExtCf *) MyMalloc(sizeof(aExtCf)); + (*ectmp)->line = mystrdup(start+2); + (*ectmp)->next = NULL; + start = end; + continue; + } + if (*start == 's') + { + aExtData *ectmp; + + while (ectmp = iauth_stats) + { + iauth_stats = iauth_stats->next; + MyFree(ectmp->line); + MyFree(ectmp); + } + iauth_stats = (aExtData *) + MyMalloc(sizeof(aExtData)); + iauth_stats->line = MyMalloc(60); + sprintf(iauth_stats->line, + "iauth modules statistics (%s)", + myctime(timeofday)); + iauth_stats->next = (aExtData *) + MyMalloc(sizeof(aExtData)); + iauth_stats->next->line = MyMalloc(60); + sprintf(iauth_stats->next->line, + "spawned: %d, current options: %X (%.11s)", + iauth_spawn, iauth_options, + (iauth_version) ? iauth_version : "???"); + iauth_stats->next->next = NULL; + start = end; + continue; + } + if (*start == 'S') + { + aExtData **ectmp = &iauth_stats; + + while (*ectmp) + ectmp = &((*ectmp)->next); + *ectmp = (aExtData *) MyMalloc(sizeof(aExtData)); + (*ectmp)->line = mystrdup(start+2); + (*ectmp)->next = NULL; + start = end; + continue; + } + if (*start != 'U' && *start != 'u' && *start != 'o' && + *start != 'K' && *start != 'k' && + *start != 'D') + { + sendto_flag(SCH_AUTH, "Garbage from iauth [%s]", + start); + sendto_iauth("-1 E Garbage [%s]", start); + /* + ** The above should never happen, but i've seen it + ** occasionnally, so let's try to get more info + ** about it! -kalt + */ + sendto_flag(SCH_AUTH, + "last=%u start=%x end=%x buf=%x olen=%d i=%d", + last, start, end, buf, olen, i); + sendto_iauth( + "-1 E last=%u start=%x end=%x buf=%x olen=%d i=%d", + last, start, end, buf, olen, i); + start = end; + continue; + } + if ((cptr = local[i = atoi(start+2)]) == NULL) + { + /* this is fairly common and can be ignored */ + if (ia_dbg) + { + sendto_flag(SCH_AUTH, "Client %d is gone.", + i); + sendto_iauth("%d E Gone [%s]", i, start); + } + start = end; + continue; + } + sprintf(tbuf, "%c %d %s %u ", start[0], i, + inetntoa((char *)&cptr->ip), cptr->port); + if (strncmp(tbuf, start, strlen(tbuf))) + { + /* this is fairly common and can be ignored */ + if (ia_dbg) + { + sendto_flag(SCH_AUTH, + "Client mismatch: %d [%s] != [%s]", + i, start, tbuf); + sendto_iauth("%d E Mismatch [%s] != [%s]", + i, start, tbuf); + } + start = end; + continue; + } + if (start[0] == 'U') + { + if (*(start+strlen(tbuf)) == '\0') + { + sendto_flag(SCH_AUTH, + "Null U message! %d [%s]", + i, start); + sendto_iauth("%d E Null U [%s]", i, start); + start = end; + continue; + } + if (cptr->auth != cptr->username) + { + istat.is_authmem -= sizeof(cptr->auth); + istat.is_auth -= 1; + MyFree(cptr->auth); + } + cptr->auth = mystrdup(start+strlen(tbuf)); + set_clean_username(cptr); + cptr->flags |= FLAGS_GOTID; + } + else if (start[0] == 'u') + { + if (*(start+strlen(tbuf)) == '\0') + { + sendto_flag(SCH_AUTH, + "Null u message! %d [%s]", + i, start); + sendto_iauth("%d E Null u [%s]", i, start); + start = end; + continue; + } + if (cptr->auth != cptr->username) + { + istat.is_authmem -= sizeof(cptr->auth); + istat.is_auth -= 1; + MyFree(cptr->auth); + } + cptr->auth = MyMalloc(strlen(start+strlen(tbuf)) + + 2); + *cptr->auth = '-'; + strcpy(cptr->auth+1, start+strlen(tbuf)); + set_clean_username(cptr); + cptr->flags |= FLAGS_GOTID; + } + else if (start[0] == 'o') + { + if (!WaitingXAuth(cptr)) + { + sendto_flag(SCH_AUTH, + "Early o message discarded!"); + sendto_iauth("%d E Early o [%s]", i,start); + start = end; + continue; + } + if (cptr->user == NULL) + { + /* just to be safe */ + sendto_flag(SCH_AUTH, + "Ack! cptr->user is NULL"); + start = end; + continue; + } + strncpyzt(cptr->user->username, tbuf, USERLEN+1); + } + else if (start[0] == 'D') + { + /*authentication finished*/ + ClearXAuth(cptr); + SetDoneXAuth(cptr); + if (WaitingXAuth(cptr)) + { + ClearWXAuth(cptr); + register_user(cptr, cptr, cptr->name, + cptr->user->username); + } + else + ClearWXAuth(cptr); + } + else + { + /* + ** mark for kill, because it cannot be killed + ** yet: we don't even know if this is a server + ** or a user connection! + */ + if (start[0] == 'K') + cptr->exitc = EXITC_AREF; + else + cptr->exitc = EXITC_AREFQ; + /* should also check to make sure it's still + an unregistered client.. */ + /* should be extended to work after registration */ + } + start = end; + } + if (start != buf+olen) + bcopy(start, obuf, olen = (buf+olen)-start+1); + else + olen = 0; + } +} + +/* + * report_iauth_conf + * + * called from m_stats(), this is the reply to /stats a + */ +void +report_iauth_conf(sptr, to) +aClient *sptr; +char *to; +{ + aExtCf *ectmp = iauth_conf; + + if (adfd < 0) + return; + while (ectmp) + { + sendto_one(sptr, ":%s %d %s :%s", + ME, RPL_STATSIAUTH, to, ectmp->line); + ectmp = ectmp->next; + } +} + +/* + * report_iauth_stats + * + * called from m_stats(), this is part of the reply to /stats t + */ +void +report_iauth_stats(sptr, to) +aClient *sptr; +char *to; +{ + aExtData *ectmp = iauth_stats; + + while (ectmp) + { + sendto_one(sptr, ":%s %d %s :%s", + ME, RPL_STATSDEBUG, to, ectmp->line); + ectmp = ectmp->next; + } +} +#endif + +/* + * start_auth + * + * Flag the client to show that an attempt to contact the ident server on + * the client's host. The connect and subsequently the socket are all put + * into 'non-blocking' mode. Should the connect or any later phase of the + * identifing process fail, it is aborted and the user is given a username + * of "unknown". + */ +void start_auth(cptr) +Reg aClient *cptr; +{ +#ifndef NO_IDENT + struct SOCKADDR_IN us, them; + + SOCK_LEN_TYPE ulen, tlen; + +# if defined(USE_IAUTH) + if ((iauth_options & XOPT_REQUIRED) && adfd < 0) + return; +# endif + Debug((DEBUG_NOTICE,"start_auth(%x) fd %d status %d", + cptr, cptr->fd, cptr->status)); + if ((cptr->authfd = socket(AFINET, SOCK_STREAM, 0)) == -1) + { +# ifdef USE_SYSLOG + syslog(LOG_ERR, "Unable to create auth socket for %s:%m", + get_client_name(cptr,TRUE)); +# endif + Debug((DEBUG_ERROR, "Unable to create auth socket for %s:%s", + get_client_name(cptr, TRUE), + strerror(get_sockerr(cptr)))); + ircstp->is_abad++; + return; + } + if (cptr->authfd >= (MAXCONNECTIONS - 2)) + { + sendto_flag(SCH_ERROR, "Can't allocate fd for auth on %s", + get_client_name(cptr, TRUE)); + (void)close(cptr->authfd); + return; + } + + set_non_blocking(cptr->authfd, cptr); + + /* get remote host peer - so that we get right interface -- jrg */ + tlen = ulen = sizeof(us); + if (getpeername(cptr->fd, (struct sockaddr *)&them, &tlen) < 0) + { + /* we probably don't need this error message -kalt */ + report_error("getpeername for auth request %s:%s", cptr); + close(cptr->authfd); + cptr->authfd = -1; + return; + } + them.SIN_FAMILY = AFINET; + + /* We must bind the local end to the interface that they connected + to: The local system might have more than one network address, + and RFC931 check only sends port numbers: server takes IP addresses + from query socket -- jrg */ + (void)getsockname(cptr->fd, (struct sockaddr *)&us, &ulen); + us.SIN_FAMILY = AFINET; +# if defined(USE_IAUTH) + if (adfd >= 0) + { + char abuf[BUFSIZ]; +# ifdef INET6 + sprintf(abuf, "%d C %s %u ", cptr->fd, + inetntop(AF_INET6, (char *)&them.sin6_addr, mydummy, + MYDUMMY_SIZE), ntohs(them.SIN_PORT)); + sprintf(abuf+strlen(abuf), "%s %u", + inetntop(AF_INET6, (char *)&us.sin6_addr, mydummy, + MYDUMMY_SIZE), ntohs(us.SIN_PORT)); +# else + sprintf(abuf, "%d C %s %u ", cptr->fd, + inetntoa((char *)&them.sin_addr),ntohs(them.SIN_PORT)); + sprintf(abuf+strlen(abuf), "%s %u", + inetntoa((char *)&us.sin_addr), ntohs(us.SIN_PORT)); +# endif + if (sendto_iauth(abuf) == 0) + { + close(cptr->authfd); + cptr->authfd = -1; + cptr->flags |= FLAGS_XAUTH; + return; + } + } +# endif +# ifdef INET6 + Debug((DEBUG_NOTICE,"auth(%x) from %s %x %x", + cptr, inet_ntop(AF_INET6, (char *)&us.sin6_addr, mydummy, + MYDUMMY_SIZE), us.sin6_addr.s6_addr[14], + us.sin6_addr.s6_addr[15])); +# else + Debug((DEBUG_NOTICE,"auth(%x) from %s", + cptr, inetntoa((char *)&us.sin_addr))); +# endif + them.SIN_PORT = htons(113); + us.SIN_PORT = htons(0); /* bind assigns us a port */ + if (bind(cptr->authfd, (struct SOCKADDR *)&us, ulen) >= 0) + { + (void)getsockname(cptr->fd, (struct SOCKADDR *)&us, &ulen); +# ifdef INET6 + Debug((DEBUG_NOTICE,"auth(%x) to %s", + cptr, inet_ntop(AF_INET6, (char *)&them.sin6_addr, + mydummy, MYDUMMY_SIZE))); +# else + Debug((DEBUG_NOTICE,"auth(%x) to %s", + cptr, inetntoa((char *)&them.sin_addr))); +# endif + (void)alarm((unsigned)4); + if (connect(cptr->authfd, (struct SOCKADDR *)&them, + tlen) == -1 && errno != EINPROGRESS) + { +# ifdef INET6 + Debug((DEBUG_ERROR, + "auth(%x) connect failed to %s - %d", cptr, + inet_ntop(AF_INET6, (char *)&them.sin6_addr, + mydummy, MYDUMMY_SIZE), errno)); +# else + Debug((DEBUG_ERROR, + "auth(%x) connect failed to %s - %d", cptr, + inetntoa((char *)&them.sin_addr), errno)); +# endif + ircstp->is_abad++; + /* + * No error report from this... + */ + (void)alarm((unsigned)0); + (void)close(cptr->authfd); + cptr->authfd = -1; + return; + } + (void)alarm((unsigned)0); + } + else + { + report_error("binding stream socket for auth request %s:%s", + cptr); +# ifdef INET6 + Debug((DEBUG_ERROR,"auth(%x) bind failed on %s port %d - %d", + cptr, inet_ntop(AF_INET6, (char *)&us.sin6_addr, + mydummy, MYDUMMY_SIZE), + ntohs(us.SIN_PORT), errno)); +# else + Debug((DEBUG_ERROR,"auth(%x) bind failed on %s port %d - %d", + cptr, inetntoa((char *)&us.sin_addr), + ntohs(us.SIN_PORT), errno)); +# endif + } + + cptr->flags |= (FLAGS_WRAUTH|FLAGS_AUTH); + if (cptr->authfd > highest_fd) + highest_fd = cptr->authfd; +#endif + return; +} + +/* + * send_authports + * + * Send the ident server a query giving "theirport , ourport". + * The write is only attempted *once* so it is deemed to be a fail if the + * entire write doesn't write all the data given. This shouldnt be a + * problem since the socket should have a write buffer far greater than + * this message to store it in should problems arise. -avalon + */ +void send_authports(cptr) +aClient *cptr; +{ + struct SOCKADDR_IN us, them; + + char authbuf[32]; + SOCK_LEN_TYPE ulen, tlen; + + Debug((DEBUG_NOTICE,"write_authports(%x) fd %d authfd %d stat %d", + cptr, cptr->fd, cptr->authfd, cptr->status)); + tlen = ulen = sizeof(us); + if (getsockname(cptr->fd, (struct SOCKADDR *)&us, &ulen) || + getpeername(cptr->fd, (struct SOCKADDR *)&them, &tlen)) + { +#ifdef USE_SYSLOG + syslog(LOG_ERR, "auth get{sock,peer}name error for %s:%m", + get_client_name(cptr, TRUE)); +#endif + goto authsenderr; + } + + SPRINTF(authbuf, "%u , %u\r\n", + (unsigned int)ntohs(them.SIN_PORT), + (unsigned int)ntohs(us.SIN_PORT)); + +#ifdef INET6 + Debug((DEBUG_SEND, "sending [%s] to auth port %s.113", + authbuf, inet_ntop,(AF_INET6, (char *)&them.sin6_addr, + mydummy, MYDUMMY_SIZE))); +#else + Debug((DEBUG_SEND, "sending [%s] to auth port %s.113", + authbuf, inetntoa((char *)&them.sin_addr))); +#endif + if (write(cptr->authfd, authbuf, strlen(authbuf)) != strlen(authbuf)) + { +authsenderr: + ircstp->is_abad++; + (void)close(cptr->authfd); + if (cptr->authfd == highest_fd) + while (!local[highest_fd]) + highest_fd--; + cptr->authfd = -1; + cptr->flags &= ~(FLAGS_AUTH|FLAGS_WRAUTH); + return; + } + cptr->flags &= ~FLAGS_WRAUTH; + return; +} + +/* + * read_authports + * + * read the reply (if any) from the ident server we connected to. + * The actual read processijng here is pretty weak - no handling of the reply + * if it is fragmented by IP. + */ +void read_authports(cptr) +Reg aClient *cptr; +{ + Reg char *s, *t; + Reg int len; + char ruser[513], system[8]; + u_short remp = 0, locp = 0; + + *system = *ruser = '\0'; + Debug((DEBUG_NOTICE,"read_authports(%x) fd %d authfd %d stat %d", + cptr, cptr->fd, cptr->authfd, cptr->status)); + /* + * Nasty. Can't allow any other reads from client fd while we're + * waiting on the authfd to return a full valid string. Use the + * client's input buffer to buffer the authd reply. + * Oh. this is needed because an authd reply may come back in more + * than 1 read! -avalon + */ + if ((len = read(cptr->authfd, cptr->buffer + cptr->count, + sizeof(cptr->buffer) - 1 - cptr->count)) >= 0) + { + cptr->count += len; + cptr->buffer[cptr->count] = '\0'; + } + + if ((len > 0) && (cptr->count != (sizeof(cptr->buffer) - 1)) && + (sscanf(cptr->buffer, "%hd , %hd : USERID : %*[^:]: %512s", + &remp, &locp, ruser) == 3)) + { + s = rindex(cptr->buffer, ':'); + *s++ = '\0'; + for (t = (rindex(cptr->buffer, ':') + 1); *t; t++) + if (!isspace(*t)) + break; + strncpyzt(system, t, sizeof(system)); + for (t = ruser; *s && (t < ruser + sizeof(ruser)); s++) + if (!isspace(*s) && *s != ':') + *t++ = *s; + *t = '\0'; + Debug((DEBUG_INFO,"auth reply ok [%s] [%s]", system, ruser)); + } + else if (len != 0) + { + if (!index(cptr->buffer, '\n') && !index(cptr->buffer, '\r')) + return; + Debug((DEBUG_ERROR,"local %d remote %d s %x", + locp, remp, ruser)); + Debug((DEBUG_ERROR,"bad auth reply in [%s]", cptr->buffer)); + *ruser = '\0'; + } + (void)close(cptr->authfd); + if (cptr->authfd == highest_fd) + while (!local[highest_fd]) + highest_fd--; + cptr->count = 0; + cptr->authfd = -1; + ClearAuth(cptr); + if (len > 0) + Debug((DEBUG_INFO,"ident reply: [%s]", cptr->buffer)); + + if (!locp || !remp || !*ruser) + { + ircstp->is_abad++; + return; + } + ircstp->is_asuc++; + if (cptr->auth != cptr->username)/*impossible, but...*/ + { + istat.is_authmem -= sizeof(cptr->auth); + istat.is_auth -= 1; + MyFree(cptr->auth); + } + if (!strncmp(system, "OTHER", 5)) + { /* OTHER type of identifier */ + cptr->auth = MyMalloc(strlen(ruser) + 2); + *cptr->auth = '-'; + strcpy(cptr->auth+1, ruser); + } + else + cptr->auth = mystrdup(ruser); + set_clean_username(cptr); + cptr->flags |= FLAGS_GOTID; + Debug((DEBUG_INFO, "got username [%s]", ruser)); + return; +} |