; ; IPv6 patch for cfingerd 1.4.3 daemon. ; System with working getaddrinfo(), getnameinfo(), ; functions and defined struct sockaddr_storage required ; (ie. Linux 2.2.x with glibc 2.1.1 or *BSD/KAME) ; ; Arkadiusz Mi¶kiewicz , PLD/Linux ; Index: cfingerd/CHANGES diff -u cfingerd/CHANGES:1.1.1.1 cfingerd/CHANGES:1.2 --- cfingerd/CHANGES:1.1.1.1 Sun Dec 12 12:23:27 1999 +++ cfingerd/CHANGES Sun Dec 12 13:07:49 1999 @@ -1,3 +1,6 @@ +CFINGERD 1.4.4 ??/??/?? + - full IPv6 support ! + CFINGERD 1.4.3 09/29/99 - Security update, fixes buffer overflows introduced with sscanf(), exploitable under FreeBSD Index: cfingerd/src/cfingerd.h diff -u cfingerd/src/cfingerd.h:1.1.1.1 cfingerd/src/cfingerd.h:1.2 --- cfingerd/src/cfingerd.h:1.1.1.1 Sun Dec 12 12:23:27 1999 +++ cfingerd/src/cfingerd.h Sun Dec 12 13:07:49 1999 @@ -28,6 +28,9 @@ #include #include #include +#ifdef INET6 +#include +#endif #include #include @@ -278,10 +281,20 @@ extern CONFIG prog_config; extern ECRUFT errors[]; +#ifdef INET6 +extern char *localhost, *ident_user; +extern char remote_addr[100], ip_address[100]; +#else extern char *remote_addr, *localhost, *ident_user, *ip_address; +#endif extern int trusted_host_num, rejected_host_num, forward_host_num, +#ifndef INET6 fakeuser_num, num_finger_sites, num_headers, local_port, remote_port, can_log; +#else + fakeuser_num, num_finger_sites, num_headers, can_log; +extern char local_port[32], remote_port[32]; +#endif extern FILE *top_display, *bottom_display, *noname_display, *nouser_display, *rejected_display, *identd_display; extern BOOL local_finger, emulated; Index: cfingerd/src/main.c diff -u cfingerd/src/main.c:1.1.1.1 cfingerd/src/main.c:1.2 --- cfingerd/src/main.c:1.1.1.1 Sun Dec 12 12:23:27 1999 +++ cfingerd/src/main.c Sun Dec 12 13:07:49 1999 @@ -1,6 +1,7 @@ /* * CFINGERD * Main Routine + * IPv6 code Copyright (C) 1999 Arkadiusz Mi¶kiewicz * * 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 @@ -20,11 +21,21 @@ #include "privs.h" CONFIG prog_config; +#ifdef INET6 +char *localhost, *ident_user; +char remote_addr[100]; +char ip_address[100]; +#else char *remote_addr, *localhost, *ident_user, *ip_address; +#endif FILE *top_display, *bottom_display, *noname_display, *nouser_display, *rejected_display, *identd_display; BOOL local_finger, emulated; +#ifdef INET6 +char local_port[32], remote_port[32]; +#else int local_port, remote_port; +#endif unsigned short listen_port; unsigned long listen_addr; @@ -62,6 +73,7 @@ char line[100], username[80], syslog_str[200]; int un_type; char *cp; +#ifndef INET6 struct sockaddr_in local_addr; struct servent *serv; @@ -70,6 +82,9 @@ else listen_port = htons(79); listen_addr = htonl(INADDR_ANY); +#else + struct sockaddr_storage local_addr; +#endif /* Initialize CFINGERD */ start_handler(); @@ -147,8 +162,12 @@ /* If we're not doing emulated stuff, we can assume that we are running either as a daemon, or under INETD. In that case... */ if (!emulated) { +#ifdef INET6 + struct sockaddr_storage socket_addr; +#else struct sockaddr_in socket_addr; struct hostent *host_ent; +#endif int psize = 0; /* Can't run from command line (but this should already be checked) */ @@ -156,9 +175,19 @@ if (getsockname(0, (struct sockaddr *) &local_addr, &psize)) { syslog(LOG_WARNING, "getsockname: %s", strerror(errno)); +#ifndef INET6 local_port = 0; } else local_port = ntohs(local_addr.sin_port); +#else + snprintf(local_port, sizeof(local_port), "0"); + } else { + getnameinfo((struct sockaddr *)&local_addr, + (socket_addr.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in), + NULL, 0, local_port, sizeof(local_port), + NI_NUMERICHOST|NI_NUMERICSERV); + } +#endif if (getpeername(0, (struct sockaddr *) &socket_addr, &psize)) { printf("Internal error - not running as either a daemon or under INETD.\n"); @@ -166,6 +195,7 @@ closelog(); log(LOG_ERROR, "getpeername: ", strerror(errno)); exit(PROGRAM_BUG); +#ifndef INET6 } else remote_port = ntohs(socket_addr.sin_port); @@ -174,7 +204,14 @@ /* Get our host entry */ host_ent = (struct hostent *) gethostbyaddr((char *) &socket_addr.sin_addr, sizeof(socket_addr.sin_addr), AF_INET); - +#else + } else { + getnameinfo((struct sockaddr *)&socket_addr, + (socket_addr.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in), + ip_address, sizeof(ip_address), remote_port, sizeof(remote_port), + NI_NUMERICHOST|NI_NUMERICSERV); + } +#endif /* And get our local-host name */ #ifndef ACTUAL_HOSTNAME localhost = get_localhost(); @@ -182,16 +219,31 @@ localhost = ACTUAL_HOSTNAME; #endif +#ifndef INET6 /* Make sure we can get the remote host's address name */ if (host_ent == NULL) { remote_addr = inettos(socket_addr.sin_addr.s_addr); +#else + /* Get our host entry and make sure we can get the remote host's address name */ + if (getnameinfo((struct sockaddr *)&socket_addr, + (socket_addr.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in), + remote_addr, sizeof(remote_addr), NULL, 0, NI_NAMEREQD) != 0) { + getnameinfo((struct sockaddr *)&socket_addr, + (socket_addr.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in), + remote_addr, sizeof(remote_addr), + NULL, 0, NI_NUMERICHOST); +#endif syslog(LOG_WARNING, "%s %s", prog_config.p_strings[D_IP_NO_MATCH], remote_addr); if (!(prog_config.config_bits2 & SHOW_IP_MATCH)) CF_ERROR(E_NOIP); +#ifndef INET6 } else remote_addr = (char *) host_ent->h_name; +#else + } +#endif /* Convert any uppercase letters in the hostname to lowercase */ for (cp = remote_addr; *cp; cp++) @@ -202,6 +254,9 @@ if (!strncasecmp(remote_addr, "127.0.0.1", 9) || !strncasecmp(remote_addr, "localhost", 9) || !strncasecmp(remote_addr, "127.0.0.0", 9) || /* KTH 07/26/96 */ +#ifdef INET6 + !strncasecmp(remote_addr, "::1", 3) || +#endif !strncasecmp(remote_addr, localhost, strlen(localhost))) local_finger = TRUE; else @@ -220,11 +275,25 @@ memset (ident_user, 0, sizeof (ident_user)); strcpy (ident_user, "emulated"); #ifndef ACTUAL_LOOPBACK +#ifdef INET6 + sprintf(remote_addr, "127.0.0.1"); +#else remote_addr = "127.0.0.1"; +#endif +#else +#ifdef INET6 + sprintf(remote_addr, ACTUAL_LOOPBACK); #else remote_addr = ACTUAL_LOOPBACK; #endif +#endif } + +#ifdef INET6 + /* If IPv4-mapped IPv6 address convert it to clean IPv4 */ + if (strncmp(remote_addr, "::ffff:", 7) == 0) + strncpy(remote_addr, remote_addr + 7, sizeof(remote_addr)); +#endif /* Now, let's check to make sure this site is trusted */ if ((!local_finger) && !emulated) Index: cfingerd/src/options.c diff -u cfingerd/src/options.c:1.1.1.1 cfingerd/src/options.c:1.2 --- cfingerd/src/options.c:1.1.1.1 Sun Dec 12 12:23:27 1999 +++ cfingerd/src/options.c Sun Dec 12 13:07:49 1999 @@ -1,6 +1,7 @@ /* * CFINGERD * Starting option routines + * IPv6 code Copyright (C) 1999 Arkadiusz Mi¶kiewicz * * 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 @@ -29,10 +30,19 @@ #ifdef DAEMON_MODE pid_t pid; int i, lsock, fd, clen; +#ifdef INET6 + int err = -1; +#endif int oval = 1; +#ifdef INET6 + struct sockaddr_storage caddr; + struct addrinfo hints, *res, *res0; +#else struct sockaddr caddr; struct sockaddr_in laddr; +#endif +#ifndef INET6 if ((lsock = socket (AF_INET, SOCK_STREAM, 0)) < 0) { syslog (LOG_ERR,"can't open socket: %m"); exit (1); @@ -51,6 +61,31 @@ syslog (LOG_ERR,"can't bind: %m"); exit (1); } +#else + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + if(getaddrinfo(NULL, "79", &hints, &res0) < 0) { + syslog(LOG_ERR, "getaddrinfo() failed"); + exit(1); + } else { + for (res = res0; res; res = res->ai_next) { + if (res->ai_family == AF_UNIX) + continue; + if ((lsock = socket (res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) + continue; + if (bind (lsock, res->ai_addr, res->ai_addrlen) == 0) { + err = 0; + break; + } + } + freeaddrinfo(res0); + if (err < 0) { + syslog (LOG_ERR,"can't create socket or bind: %m"); + exit (1); +} +#endif if ((pid = fork()) < 0) { syslog (LOG_ERR, "can't fork(): %m"); @@ -77,20 +112,41 @@ while (1) { +#ifdef INET6 + fd = accept (lsock, (struct sockaddr *)&caddr, &clen); +#else fd = accept (lsock, &caddr, &clen); +#endif if (fd < 0) { #ifndef __FreeBSD__ if (errno == EPROTO) { #else if (errno == EPROTOTYPE) { #endif /* !__FreeBSD__ */ +#ifdef INET6 + getnameinfo((struct sockaddr *)&caddr, (caddr.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in), + remote_addr, sizeof(remote_addr), NULL, 0, NI_NUMERICHOST); + /* If IPv4-mapped IPv6 address convert it to clean IPv4 */ + if (strncmp(remote_addr, "::ffff:", 7) == 0) + strncpy(remote_addr, remote_addr + 7, sizeof(remote_addr)); +#else remote_addr = inet_ntoa (((struct sockaddr_in *)&caddr)->sin_addr); +#endif syslog(LOG_ERR,"failed connect (possible port scan) from %s: %m", remote_addr); } continue; } + +#ifdef INET6 + getnameinfo((struct sockaddr *)&caddr, (caddr.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in), + remote_addr, sizeof(remote_addr), NULL, 0, NI_NUMERICHOST); + /* If IPv4-mapped IPv6 address convert it to clean IPv4 */ + if (strncmp(remote_addr, "::ffff:", 7) == 0) + strncpy(remote_addr, remote_addr + 7, sizeof(remote_addr)); +#else remote_addr = inet_ntoa (((struct sockaddr_in *)&caddr)->sin_addr); +#endif /* * FIXME: Support for tcp wrapper via hosts.allow and .deny is missing Index: cfingerd/src/proto.h diff -u cfingerd/src/proto.h:1.1.1.1 cfingerd/src/proto.h:1.2 --- cfingerd/src/proto.h:1.1.1.1 Sun Dec 12 12:23:27 1999 +++ cfingerd/src/proto.h Sun Dec 12 13:07:49 1999 @@ -45,7 +45,11 @@ void become_nobody(void); void become_user(char *); int wildmat(char *, char *); +#ifdef INET6 +char *get_rfc1413_data(struct sockaddr_storage ); +#else char *get_rfc1413_data(struct sockaddr_in ); +#endif void check_unknown(char *); void log(int, char *, char *); void userlog(uid_t, gid_t, char *, char *); Index: cfingerd/src/rfc1413.c diff -u cfingerd/src/rfc1413.c:1.1.1.1 cfingerd/src/rfc1413.c:1.2 --- cfingerd/src/rfc1413.c:1.1.1.1 Sun Dec 12 12:23:27 1999 +++ cfingerd/src/rfc1413.c Sun Dec 12 13:07:49 1999 @@ -1,6 +1,7 @@ /* * CFINGERD * RFC1413 implementation + * IPv6 code Copyright (C) 1999 Arkadiusz Mi¶kiewicz * * 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 @@ -15,10 +16,18 @@ #include "cfingerd.h" +#ifdef INET6 +static jmp_buf ident_alarm; +#endif + void rfc1413_alarm(int signal) { +#ifdef INET6 + siglongjmp(ident_alarm, 1); +#else if (signal == SIGALRM) ident_user = "unknown@alarm.signal"; +#endif } /* Self contained RFC1413 implementation. Thanks to Joel Katz for parts of @@ -26,17 +35,28 @@ * contained in a single program. Simple, easy to use. */ #define BUFLEN 256 +#ifdef INET6 +char *get_rfc1413_data( struct sockaddr_storage local_addr ) +{ + static int j = -1, err = -1; +#else char *get_rfc1413_data( struct sockaddr_in local_addr ) { int i, j; struct sockaddr_in sin; +#endif char buffer[1024], buf[BUFLEN], uname[64], *bleah; char *cp, *xp; +#ifdef INET6 + struct addrinfo hints, *res, *res0; +#else struct servent *serv; +#endif bleah = (char *) malloc(BUFLEN); memset(bleah, 0, BUFLEN); +#ifndef INET6 j = socket(AF_INET, SOCK_STREAM, 0); if (j < 2) { snprintf(bleah, BUFLEN, "unknown@%s", remote_addr); @@ -71,8 +91,54 @@ alarm(0); return(bleah); } + + snprintf(buffer, sizeof(buffer), "%d,%d\n", remote_port, local_port); +#else + memset(&hints, 0, sizeof(hints)); + hints.ai_family = local_addr.ss_family; + hints.ai_socktype = SOCK_STREAM; + if((err = getaddrinfo(ip_address, "113", &hints, &res0)) < 0) { + syslog(LOG_ERR, "rfc1413-getaddrinfo: %s", gai_strerror(err)); + snprintf(bleah, BUFLEN, "unknown@%s", remote_addr); + return(bleah); + } + err = -1; + + if (sigsetjmp(ident_alarm, 1) != 0) { + snprintf(bleah, BUFLEN, "alarm.signal@%s", remote_addr); + return(bleah); + } + signal(SIGALRM, rfc1413_alarm); + alarm(5); + for (res = res0; res; res = res->ai_next) { + if ((j = socket(res->ai_family, res->ai_socktype, res->ai_protocol))) + continue; + switch(local_addr.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&local_addr)->sin_port = 0; + break; + case AF_INET6: + ((struct sockaddr_in6 *)&local_addr)->sin6_port = 0; + break; + } + bind(j, (struct sockaddr *)&local_addr, (local_addr.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); + if (connect(j, res->ai_addr, res->ai_addrlen) < 0) + close(j); + else { + err = 0; + break; + } + } + freeaddrinfo(res0); + if (err < 0) { + syslog(LOG_ERR, "rfc1413-socket-or-connect problem"); + snprintf(bleah, BUFLEN, "unknown@%s", remote_addr); + alarm(0); + return(bleah); + } - snprintf(buffer, sizeof(buffer), "%d,%d\n", remote_port, local_port); + snprintf(buffer, sizeof(buffer), "%s,%s\n", remote_port, local_port); +#endif write(j, buffer, strlen(buffer)); memset(buf, 0, sizeof(buf)); Index: cfingerd/userlist/display.c diff -u cfingerd/userlist/display.c:1.1.1.1 cfingerd/userlist/display.c:1.2 --- cfingerd/userlist/display.c:1.1.1.1 Sun Dec 12 12:23:27 1999 +++ cfingerd/userlist/display.c Sun Dec 12 13:06:29 1999 @@ -60,6 +60,7 @@ int i; char *our_host; char *idle; + char *cp, *x; our_host = get_localhost(); if (times_on == 0) {