diff -urN noip-2.1.9.patched/noip2.c noip-2.1.9/noip2.c --- noip-2.1.9.patched/noip2.c 2007-08-28 00:54:19.000000000 +0300 +++ noip-2.1.9/noip2.c 2008-11-22 00:19:54.000000000 +0200 @@ -112,6 +112,17 @@ + instances were not diplayed due to stderr being closed + added version number into shared mem and -S display + + November 2008 (johna) still version 2.1.8 + + reworked forced update code to use 'wget' and the + + hostactivate.php script at www.no-ip.com + + added check of returned IP address in get_our_visble_IP_addr + + hardened GetNextLine to prevent possible buffer overflow + + + November 2008 (johna) version 2.1.9 + + hardened force_update() to prevent possible buffer overflow + + hardened autoconf() the same way + + patch suggested by xenomuta@phreaker.net + */ ///////////////////////////////////////////////////////////////////////////// @@ -166,8 +177,8 @@ #define DEBUG #define ENCRYPT 1 +#define FORCE_UPDATE 0 -#undef MAX #define MAX(x,y) (((x)>(y))?(x):(y)) #define READ_TIMEOUT 90 @@ -181,10 +192,12 @@ #define NOIP_NAME "dynupdate.no-ip.com" +#define UPD_NAME "www.no-ip.com" +#define UPD_SCRIPT "hostactive.php" #define NOIP_IP_SCRIPT "ip1.dynupdate.no-ip.com" #define CLIENT_IP_PORT 8245 -#define VERSION "2.1.7" +#define VERSION "2.1.9" #define USER_AGENT "User-Agent: Linux-DUC/"VERSION #define SETTING_SCRIPT "settings.php?" #define USTRNG "username=" @@ -218,6 +231,7 @@ #define NODNSGROUP "@@NO_GROUP@@" #define HOST 1 #define GROUP 2 +#define DOMAIN 3 #ifndef PREFIX #define PREFIX "/usr/local" #endif @@ -232,6 +246,7 @@ #define NOIP_KEY 0x50494f4e #define SHMEM_SIZE (MAX_INSTANCE * sizeof(struct INSTANCE)) #define DEFAULT_NAT_INTERVAL 30 +#define WGET_PGM "/usr/bin/wget" #define SPACE ' ' #define EQUALS '=' @@ -269,6 +284,7 @@ #define BADCONFIG4 -14 #define BADCONFIG5 -15 #define BADCONFIG6 -16 +#define BADACTIVATE -20 #define CMSG01 "Can't locate configuration file %s. (Try -c). Ending!\n" #define CMSG02 "'%s' is a badly constructed configuration file. Ending!\n" @@ -312,6 +328,9 @@ #define CMSG40 "Do you wish to run something at successful update?[N] (y/N) " #define CMSG40a "Please enter the script/program name " #define CMSG41 "Exec path unreadable in '%s'. Ending!\n" +#define CMSG42 "Can't get our visible IP address from %s" +#define CMSG43 "Invalid domain name '%s'" +#define CMSG44 "Invalid host name '%s'" #define CMSG99 "\nThis client has expired. Please go to http://www.no-ip.com/downloads.php" #define CMSG99a "to get our most recent version.\n" @@ -342,7 +361,7 @@ int reqnum = 0; int prompt_for_executable = 1; int shm_dump_active = 0; -int Force_Update = 0; // force initial action +int Force_Update = FORCE_INTERVAL; int dummy = 0; void *shmaddr = NULL; char *program = NULL; @@ -447,6 +466,7 @@ /////////////////////////////////////////////////////////////////////////// void process_options(int argc, char *argv[]); +void update_handler(int signum); void alarm_handler(int signum); void sigchld_handler(int signum); void exit_handler(int signum); @@ -456,6 +476,7 @@ void save_IP(); void dump_shm(struct shmid_ds *d); int get_shm_info(); +int validate_IP_addr(char *src, char *dest); void get_our_visible_IPaddr(char *dest); void getip(char *dest, char *device); int run_as_background(); @@ -474,6 +495,8 @@ ushort chksum(char *buf, unsigned int size); void dump_buffer(int x); int dynamic_update(); +int validate_name(int flag, char *name); +int force_update(); int handle_dynup_error(int error_code); int handle_config_error(int err_code); void *Malloc(int size); @@ -584,6 +607,8 @@ if (background) { sig.sa_handler = (void *)wake_handler; sigaction(SIGUSR1,&sig,NULL); + sig.sa_handler = update_handler; + sigaction(SIGUSR2,&sig,NULL); sig.sa_handler = exit_handler; sigaction(SIGINT,&sig,NULL); sigaction(SIGTERM,&sig,NULL); @@ -613,7 +638,7 @@ shmdt(shmaddr); // done with our shared memory return FATALERR; } - strcpy(my_instance->Last_IP_Addr, IPaddress); + strncpy(my_instance->Last_IP_Addr, IPaddress, IPLEN); } save_IP(); free(new_config); @@ -747,6 +772,11 @@ return; } /////////////////////////////////////////////////////////////////////////// +void update_handler(int signum) // entered on SIGUSR2 +{ + Force_Update = -1; +} +/////////////////////////////////////////////////////////////////////////// void alarm_handler(int signum) // entered on SIGALRM { timed_out = 1; @@ -956,7 +986,7 @@ } } if (!background && *IPaddress) { // update running daemon - strcpy(is->Last_IP_Addr, IPaddress); + strncpy(is->Last_IP_Addr, IPaddress, IPLEN); continue; } Msg("Configuration '%s' already in use", is->cfilename); @@ -983,8 +1013,8 @@ int run_as_background() { int x, delay; - int time_to_update; char *err_string; + static int startup = 1; x = fork(); switch (x) { @@ -999,7 +1029,8 @@ if (get_shm_info() == FATALERR) return FATALERR; log2syslog++; - fclose(stderr); + if (log2syslog > 0) + fclose(stderr); fclose(stdout); fclose(stdin); syslog(LOG_INFO, "v%s daemon started%s\n", @@ -1010,38 +1041,36 @@ get_our_visible_IPaddr(IPaddress); else getip(IPaddress, device); + if (startup) { + startup = 0; + my_instance->Last_IP_Addr[0] = '0';// force check + } #ifdef DEBUG if (my_instance->debug) { Msg("! Last_IP_Addr = %s, IP = %s",my_instance->Last_IP_Addr, IPaddress); +#if FORCE_UPDATE Msg("Force_Update == %d\n", Force_Update); +#endif } #endif - time_to_update = 0; if (*IPaddress) { - if (strcmp(IPaddress, my_instance->Last_IP_Addr) || - (Force_Update <= 0)) - time_to_update = 1; - } else { // make sure we don't lose force update trigger - if (Force_Update <= 0) - Force_Update = my_instance->interval; - } - if (time_to_update) { - if (dynamic_update() == SUCCESS) - strcpy(my_instance->Last_IP_Addr, IPaddress); - else // don't lose forced update trigger - Force_Update = MAX(Force_Update, - my_instance->interval); - if (connect_fail) - delay = MAX(300, delay); // wait at least 5 minutes more - *IPaddress = 0; + if (strcmp(IPaddress, my_instance->Last_IP_Addr)) { + if (dynamic_update() == SUCCESS) + strncpy(my_instance->Last_IP_Addr, IPaddress, + IPLEN); + if (connect_fail) + delay = MAX(300, delay); // wait at least 5 minutes more + *IPaddress = 0; + } } - if (Force_Update <= 0) { - // reset to max - Force_Update = FORCE_INTERVAL; - } else { - // one time quanta closer to update trigger +#if FORCE_UPDATE + if (Force_Update < 0) { + if (force_update() == SUCCESS) + Force_Update = FORCE_INTERVAL; + } else { // one time quanta closer to update trigger Force_Update -= my_instance->interval; } +#endif if (background) // signal may have reset this! Sleep(delay); } @@ -1479,6 +1508,38 @@ return SUCCESS; } /////////////////////////////////////////////////////////////////////////// +int validate_IP_addr(char *src, char *dest) +{ + char *p; + int i, octet; + + octet = atoi(src); + if ((octet > 255) || (octet < 0)) + return 0; // bad octet + if ((p = strchr(src, '.')) != NULL) { + octet = atoi(++p); + if ((octet > 255) || (octet < 0)) + return 0; // bad octet + if ((p = strchr(p, '.')) != NULL) { + octet = atoi(++p); + if ((octet > 255) || (octet < 0)) + return 0; // bad octet + if ((p = strchr(p, '.')) != NULL) { + octet = atoi(++p); + if ((octet <= 255) && (octet >= 0)) { + for (i=0; i<3; i++) + if (!isdigit(p[i])) + break; + p[i] = 0; // NULL terminate + strncpy(dest, src, IPLEN); + return 1; // valid IP address + } + } + } + } + return 0; // not enough dots +} +/////////////////////////////////////////////////////////////////////////// void get_our_visible_IPaddr(char *dest) { int x; @@ -1497,7 +1558,8 @@ } else { close(socket_fd); if ((p = strrchr(buffer,'\n'))) {// get end of penultimate line - strcpy(dest, ++p); // address on next line + if (!validate_IP_addr(++p, dest))// address on next line + Msg(CMSG42, NOIP_IP_SCRIPT); #ifdef DEBUG if (my_instance ? my_instance->debug : debug) fprintf(stderr,"! Our NAT IP address is %s\n", dest); @@ -1506,7 +1568,7 @@ return; // NORMAL EXIT } } - Msg("Can't get our visible IP address from %s", NOIP_IP_SCRIPT); + Msg(CMSG42, NOIP_IP_SCRIPT); return; } /////////////////////////////////////////////////////////////////////////// @@ -1533,6 +1595,7 @@ } // set up text for web page update bdecode(request, buffer); + // IPaddress has already been validated as to length and contents sprintf(tbuf, "&ip=%s", IPaddress); strcat(buffer, tbuf); // use latter half of big buffer @@ -1568,7 +1631,7 @@ *p++ = 0; // now p points at return code i = atoi(p); if (is_group) { - sprintf(gname, "group[%s]", ourname); + snprintf(gname, LINELEN-1, "group[%s]", ourname); ourname = gname; } response++; @@ -1594,6 +1657,7 @@ Msg("Can't fork (%s) to run %s", strerror(errno), buffer); exit(1); case 0: // child + // IPaddress has already been validated as to length and contents sprintf(ipbuf, "%s", IPaddress); p = strrchr(buffer, '/'); if (p) @@ -1700,6 +1764,9 @@ case WRITEFAIL: Msg("Write to %s failed (%s)", NOIP_NAME, err_string); break; + case BADACTIVATE: + Msg("Can't activate host at %s", UPD_NAME); + break; default: Msg("Unknown error %d trying to set %s at %s.", error_code, ourname, NOIP_NAME); @@ -1913,7 +1980,7 @@ { char tbuf[LINELEN]; - sprintf(tbuf, "&%s[]=%s", (kind==HOST) ? "h" : "g", p); + snprintf(tbuf, LINELEN-1, "&%s[]=%s", (kind==HOST) ? "h" : "g", p); strcat(buffer, tbuf); reqnum++; return strlen(buffer); @@ -1990,7 +2057,13 @@ ////////////////////////////////////////////////////////////////////////// void SkipHeaders() { - char *p = &buffer[offset]; + char *p; + + // global offset into buffer, set here and used only by GetNextLine ! + offset = 0; + + // position after first blank line + p = buffer; for(; *p; p++) { if(strncmp(p, "\r\n\r\n", 4) == 0) { offset += 4; @@ -2005,24 +2078,148 @@ { char *p = &buffer[offset]; char *q = dest; + char *z = &dest[LINELEN-1]; while (*p && (*p <= ' ')) { // despace & ignore blank lines p++; offset++; } - while (*p) { - *q++ = *p; + while ((*p) && (q < z)) { + *q++ = (*p) & 0x7f; // ASCII charset for text offset++; if (*p == '\n') { *q = 0; //fprintf(stderr, "LINE = %s", dest); - return 1; + return 1; // we have a line } else p++; } - if (q > dest) - return 1; - return 0; + if (q > dest) { // newline not found + if (q == z) + q--; // backup for newline and null + *q++ = '\n'; // add '\n' + *q = 0; // and null + return 1; // we have a line + } + return 0; // no line available +} +/////////////////////////////////////////////////////////////////////////// +int force_update() +{ + int x, found, retcode; + FILE *pfd; + char *l, *p, *q, domain[LINELEN], host[LINELEN]; + char ourbuffer[BIGBUFLEN], line[LINELEN], encline[2 * LINELEN]; + + retcode = SUCCESS; + bdecode(request, buffer); + l = strchr(buffer, '=') + 1; // point at login name + p = strchr(l, '&'); + *p = 0; // terminate it + p += 6; // step over null and 'pass=' + q = strchr(p, '&'); + *q = 0; // terminate password + sprintf(line, "%s%s%s%s", USTRNG, l, PWDSTRNG, p); + bencode(line, encline); + + if ((x = Connect(port_to_use)) != SUCCESS) { + handle_dynup_error(x); + return x; + } + // get the name list + sprintf(buffer, "GET http://%s/%s%s%s HTTP/1.0\r\n%s\r\n\r\n", + NOIP_NAME, SETTING_SCRIPT, REQUEST, encline, USER_AGENT); + if ((x = converse_with_web_server()) <= 0) { + handle_dynup_error(x); + return x; + } + + SkipHeaders(); + while (GetNextLine(line)) { + p = line; + if (strncmp(p, "debug) + Msg("> %s\n", ourbuffer); // display the command +#endif + // send wget command + pfd = popen(ourbuffer, "r"); + found = 0; + while (fgets(ourbuffer, BIGBUFLEN, pfd)) { +#ifdef DEBUG + if (my_instance->debug) + Msg("> %s\n", ourbuffer); // display the line +#endif + if (strstr(ourbuffer, "Update Successful!!")) { + found = 1; + syslog(LOG_INFO, "Activation of host %s.%s succeeded", + host, domain); + break; + } + } + pclose(pfd); + if (!found) + syslog(LOG_INFO, "Can't activate host %s.%s",host, domain); + } + } + close(socket_fd); + return retcode; +} +/////////////////////////////////////////////////////////////////////////// +int validate_name(int flag, char *name) +{ + char *p; + int x; + + if (flag == DOMAIN) + x = '.'; // add the dot for domains + else + x = '-'; // just re-use the hyphen for hosts + for (p=name; *p; p++) { + if (isalnum(*p) || (*p == '-') || (*p == x)) + continue; + return 0; // invalid char + } + return 1; } /////////////////////////////////////////////////////////////////////////// void url_encode(char *p_in, char *p_out) @@ -2264,7 +2461,7 @@ int x, xfd; int a, b, c; FILE *fd; - char *p, *q, line[LINELEN], encline[2 * LINELEN], *pos; + char *p, line[LINELEN], encline[2 * LINELEN], *pos; char internal_ip[IPLEN]; char external_ip[IPLEN]; struct SETTINGS *s; @@ -2311,10 +2508,10 @@ } p = buffer; if ((*p >= '0') && (*p <= '9')) { // extract IP address - q = external_ip; - while (((*p >= '0') && (*p <= '9')) || (*p == '.')) - *q++ = *p++; - *q = 0; + if (!validate_IP_addr(buffer, external_ip)) { + Msg(CMSG44, p); + exit(1); + } } #ifdef DEBUG if (debug) @@ -2519,4 +2716,4 @@ fprintf(stderr, "%s\n", errmsg); } ///////////////////////////////////////////////////////////////////////////// - +/////////////////////////////////////////////////////////////////////////////