/* set to NULL if not needed */ int urlinstStartTransfer(struct iurlinfo * ui, char * filename, char *extraHeaders) { char * buf; int fd, port; int family = -1; char * finalPrefix; struct in_addr addr; struct in6_addr addr6; char *hostname, *portstr; if (!strcmp(ui->prefix, "/")) finalPrefix = ""; else finalPrefix = ui->prefix; buf = alloca(strlen(finalPrefix) + strlen(filename) + 20); if (*filename == '/') sprintf(buf, "%s%s", finalPrefix, filename); else sprintf(buf, "%s/%s", finalPrefix, filename); logMessage(INFO, "transferring %s://%s/%s to a fd", ui->protocol == URL_METHOD_FTP ? "ftp" : "http", ui->address, buf); splitHostname(ui->address, &hostname, &portstr); if (portstr == NULL) port = -1; else port = atoi(portstr); if (inet_pton(AF_INET, hostname, &addr) >= 1) family = AF_INET; else if (inet_pton(AF_INET6, hostname, &addr6) >= 1) family = AF_INET6; else { if (mygethostbyname(hostname, &addr, AF_INET) == 0) { family = AF_INET; } else if (mygethostbyname(hostname, &addr6, AF_INET6) == 0) { family = AF_INET6; } else { logMessage(ERROR, "cannot determine address family of %s", hostname); } } if (ui->protocol == URL_METHOD_FTP) { ui->ftpPort = ftpOpen(hostname, family, ui->login ? ui->login : "******", ui->password ? ui->password : "******", NULL, port); if (ui->ftpPort < 0) return -2; fd = ftpGetFileDesc(ui->ftpPort, addr6, family, buf); if (fd < 0) { close(ui->ftpPort); return -1; } } else { #ifdef ROCKS { /* * try harder to start HTTP transfer */ int tries = 1; int rc; logMessage(INFO, "ROCKS:urlinstStartTransfer:http://%s/%s\n" "Headers:%s\n", ui->address, filename, extraHeaders); fd = -1; while ((fd < 0) && (tries < 10)) { fd = httpGetFileDesc(ui->address, -1, buf, extraHeaders); if (fd == FTPERR_FAILED_DATA_CONNECT) { /* Server busy, backoff */ sleep(60); tries = 1; continue; } logMessage(INFO, "ROCKS:urlinstStartTransfer:attempt (%d)", tries); sleep(1); ++tries; } if (fd < 0) { logMessage(ERROR, "ROCKS:urlinstStartTransfer:Failed"); rc = newtWinChoice(_("GET File Error"), _("Retry"), _("Cancel"), _("Could not get file:\n\nhttp://%s/%s\n\n%s"), ui->address, filename, ftpStrerror(fd, URL_METHOD_HTTP)); if (rc == 1) return urlinstStartTransfer(ui, filename, extraHeaders); else return -1; } } #else fd = httpGetFileDesc(hostname, port, buf, extraHeaders); if (fd < 0) return -1; #endif /* ROCKS */ } if (!FL_CMDLINE(flags)) winStatus(70, 3, _("Retrieving"), "%s %s...", _("Retrieving"), filename); return fd; }
BIO * httpsGetFileDesc(char * hostname, int port, char * remotename, char *extraHeaders, int *errorcode, char **returnedHeaders) { char *buf; char headers[4096]; char *nextChar = headers; char *hstr; int sock; int rc; int checkedCode; int headerslen; int bufsize; int byteswritten; struct loaderData_s * loaderData; SSL_CTX *ssl_context; SSL *ssl; BIO *sbio = 0; X509 *server_cert; *errorcode = 0; sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (sock < 0) { logMessage(ERROR, "ROCKS:httpsGetFileDesc:Could not get a socket"); *errorcode = FTPERR_FAILED_CONNECT; return NULL; } /* OpenSSL_add_all_algorithms(); */ SSLeay_add_ssl_algorithms(); ssl_context = SSL_CTX_new(SSLv23_client_method()); if (!ssl_context) { logMessage(ERROR, "Could not create SSLv2,3 context"); *errorcode = FTPERR_FAILED_CONNECT; goto error; } /* Pull in the Global Loader Data structure. */ loaderData = rocks_global_loaderData; /* I have a Certificate */ if (loaderData->cert_filename) { rc = SSL_CTX_use_certificate_file(ssl_context, loaderData->cert_filename, SSL_FILETYPE_PEM); if (!rc) { logMessage(ERROR, "Could not read Cluster Certificate"); *errorcode = FTPERR_CLIENT_SECURITY; goto error; } rc = SSL_CTX_use_PrivateKey_file(ssl_context, loaderData->priv_filename, SSL_FILETYPE_PEM); if (!rc) { logMessage(ERROR, "Could not read Cluster cert private key"); *errorcode = FTPERR_CLIENT_SECURITY; goto error; } /* Only connect to servers that have certs signed by * our trusted CA. */ if (loaderData->authParent) { rc = SSL_CTX_load_verify_locations(ssl_context, loaderData->ca_filename, 0); if (!rc) { logMessage(ERROR, "Could not read Server CA cert"); *errorcode = FTPERR_CLIENT_SECURITY; goto error; } SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, 0); SSL_CTX_set_verify_depth(ssl_context, 1); } } sbio = BIO_new_ssl_connect(ssl_context); if (!sbio) { logMessage(ERROR, "Could not create SSL object"); *errorcode = FTPERR_CLIENT_SECURITY; goto error; } BIO_get_ssl(sbio, &ssl); if (!ssl) { logMessage(ERROR, "Could not find ssl pointer."); *errorcode = FTPERR_CLIENT_SECURITY; goto error; } SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); BIO_set_conn_hostname(sbio, hostname); BIO_set_conn_port(sbio, "https"); rc = BIO_do_connect(sbio); if (rc<=0) { rc = SSL_get_verify_result(ssl); if (rc) { logMessage(ERROR, "Could not verify %s's identity", hostname); *errorcode = FTPERR_REFUSED; goto error; } logMessage(ERROR, "Could not connect to %s:https", hostname); *errorcode = FTPERR_FAILED_CONNECT; goto error; } rc = BIO_do_handshake(sbio); if (rc<=0) { logMessage(ERROR, "Could not establish HTTPS connection with %s", hostname); *errorcode = FTPERR_FAILED_CONNECT; goto error; } server_cert = SSL_get_peer_certificate(ssl); /* Show credentials if appropriate. */ /* Don't Show Credentials */ if ( (0 == 1) && !loaderData->cert_filename && !loaderData->ekv && !loaderData->dropCert) { rc = show_cert(server_cert); if (rc != 1) { *errorcode = FTPERR_REFUSED; goto error; } } if (extraHeaders) hstr = extraHeaders; else hstr = ""; bufsize = strlen(remotename) + strlen(hostname) + strlen(hstr) + 30; if ((buf = malloc(bufsize)) == NULL) { logMessage(ERROR, "ROCKS:httpsGetFileDesc:malloc failed"); *errorcode = FTPERR_FAILED_CONNECT; goto error; } sprintf(buf, "GET %s HTTP/1.0\r\nHost: %s\r\n%s\r\n", remotename, hostname, hstr); byteswritten = BIO_puts(sbio, buf); logMessage(INFO, "ROCKS:httpsGetFileDesc:byteswritten(%d)", byteswritten); logMessage(INFO, "ROCKS:httpsGetFileDesc:bufsize(%d)", (int)strlen(buf)); free(buf); /* This is fun; read the response a character at a time until we: 1) Get our first \r\n; which lets us check the return code 2) Get a \r\n\r\n, which means we're done */ *nextChar = '\0'; checkedCode = 0; headerslen = 0; while (!strstr(headers, "\r\n\r\n")) { if (BIO_read(sbio, nextChar, 1) != 1) { *errorcode = FTPERR_SERVER_SECURITY; goto error; } nextChar++; *nextChar = '\0'; ++headerslen; if (nextChar - headers == sizeof(headers)) { goto error; } if (!checkedCode && strstr(headers, "\r\n")) { char * start, * end; checkedCode = 1; start = headers; while (!isspace(*start) && *start) start++; if (!*start) { goto error; } while (isspace(*start) && *start) start++; end = start; while (!isspace(*end) && *end) end++; if (!*end) { goto error; } logMessage(INFO, "ROCKS:httpsGetFileDesc:status %s.", start); *end = '\0'; if (!strcmp(start, "404")) goto error; else if (!strcmp(start, "403")) { *errorcode = FTPERR_SERVER_SECURITY; goto error; } else if (!strcmp(start, "503")) { /* A server nack - busy */ logMessage(WARNING, "ROCKS:server busy"); watchdog_reset(); *errorcode = FTPERR_FAILED_DATA_CONNECT; goto error; } else if (strcmp(start, "200")) { *errorcode = FTPERR_BAD_SERVER_RESPONSE; goto error; } *end = ' '; } } if ((*returnedHeaders = (char *)malloc(headerslen + 1)) != NULL) { memcpy(*returnedHeaders, headers, headerslen + 1); } return sbio; error: close(sock); if (sbio) BIO_free_all(sbio); if (!*errorcode) *errorcode = FTPERR_SERVER_IO_ERROR; logMessage(ERROR, "ROCKS:httpsGetFileDesc:Error %s", ftpStrerror(*errorcode, URL_METHOD_HTTP)); return NULL; }
/* Use SSL. Must be entirely different since the return * type is a pointer to an SSL structure. */ BIO * urlinstStartSSLTransfer(struct iurlinfo * ui, char * filename, char *extraHeaders, int silentErrors, int flags, char *nextServer) { extern void watchdog_reset(); int tries = 1; int rc; int errorcode = -1; int sleepmin = KS_RETRY_MIN; char *returnedHeaders; BIO *sbio = 0; logMessage(INFO, "ROCKS:transferring https://%s/%s\nHeaders:%s\n", ui->address, filename, extraHeaders); /* Add 'public' path element if we dont have a cert. */ if (!haveCertificate()) addPublic(&filename); while ((errorcode < 0) && (tries < 10)) { sbio = httpsGetFileDesc(ui->address, -1, filename, extraHeaders, &errorcode, &returnedHeaders); if (errorcode == 0) { char *ptr; char trackers[256]; char pkgservers[256]; if ((ptr = strstr(returnedHeaders, "X-Avalanche-Trackers:")) != NULL) { sscanf(ptr, "X-Avalanche-Trackers: %256s", trackers); } else { if (nextServer != NULL) { snprintf(trackers, sizeof(trackers) - 1, "%s", nextServer); } else { strcpy(trackers, "127.0.0.1"); } } if ((ptr = strstr(returnedHeaders, "X-Avalanche-Pkg-Servers:")) != NULL) { sscanf(ptr, "X-Avalanche-Pkg-Servers: %256s", pkgservers); } else { if (nextServer != NULL) { snprintf(pkgservers, sizeof(pkgservers) - 1, "%s", nextServer); } else { strcpy(pkgservers, "127.0.0.1"); } } writeAvalancheInfo(trackers, pkgservers); } else if (errorcode == FTPERR_FAILED_DATA_CONNECT) { /* * read the retry value from the return message */ char *ptr; int sleeptime = 0; if ((ptr = strstr(returnedHeaders, "Retry-After:")) != NULL) { sscanf(ptr, "Retry-After: %d", &sleeptime); } if (sleeptime <= 0) { /* * Backoff a random interval between * KS_RETRY_MIN and KS_RETRY_MAX */ sleeptime = sleepmin + ((KS_RETRY_MAX - sleepmin) * (rand()/(float)RAND_MAX)); } winStatus(55, 3, _("Server Busy"), _("I will retry " "for a ks file after a %d sec sleep..."), sleeptime, 0); /* * this must be in a loop, as the alarm associated * with the watchdog timer is sending a signal which * interrupts the sleep(). */ while ((sleeptime = sleep(sleeptime)) != 0) { ; } newtPopWindow(); tries = 1; /* Don't let the watchdog fire if the kickstart server is reporting busy */ watchdog_reset(); continue; } else if (errorcode == FTPERR_REFUSED) { /* * always accept the parent credentials */ forceParentAuth(); continue; } logMessage(INFO, "ROCKS:urlinstStartSSLTransfer:attempt (%d)", tries); sleep(1); ++tries; } if (errorcode < 0) { logMessage(ERROR, "ROCKS:urlinstStartSSLTransfer:Failed"); rc = 0; /* Go through the public door automatically, but only * if we have not tried it already. */ if (errorcode == FTPERR_SERVER_SECURITY && !addPublic(&filename)) { rc = 1; } else { rc = newtWinChoice(_("GET File Error"), _("Retry"), _("Cancel"), _("Could not get file:\n\nhttps://%s/%s\n\n%s"), ui->address, filename, ftpStrerror(errorcode, URL_METHOD_HTTP)); } if (rc==1) /* Retry */ return urlinstStartSSLTransfer(ui, filename, extraHeaders, silentErrors, flags, nextServer); else /* Cancel */ return NULL; } if (!FL_CMDLINE(flags)) winStatus(70, 3, _("Retrieving"), "%s %.*s...", _("Retrieving"), 60, filename); return sbio; }
/** * Check that all sources/patches/icons exist locally, fetching if necessary. */ static int prepFetch(Spec spec) /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/ { #if defined(RPM_VENDOR_OPENPKG) /* splitted-source-directory */ const char *Smacro; #endif const char *Lmacro, *Lurlfn = NULL; const char *Rmacro, *Rurlfn = NULL; struct Source *sp; struct stat st; rpmRC rpmrc; int ec, rc; char *cp; /* XXX insure that %{_sourcedir} exists */ rpmrc = RPMRC_OK; Lurlfn = rpmGenPath(NULL, "%{?_sourcedir}", NULL); if (Lurlfn != NULL && *Lurlfn != '\0') rpmrc = rpmMkdirPath(Lurlfn, "_sourcedir"); Lurlfn = _free(Lurlfn); if (rpmrc != RPMRC_OK) return -1; /* XXX insure that %{_patchdir} exists */ rpmrc = RPMRC_OK; Lurlfn = rpmGenPath(NULL, "%{?_patchdir}", NULL); if (Lurlfn != NULL && *Lurlfn != '\0') rpmrc = rpmMkdirPath(Lurlfn, "_patchdir"); Lurlfn = _free(Lurlfn); if (rpmrc != RPMRC_OK) return -1; /* XXX insure that %{_icondir} exists */ rpmrc = RPMRC_OK; Lurlfn = rpmGenPath(NULL, "%{?_icondir}", NULL); if (Lurlfn != NULL && *Lurlfn != '\0') rpmrc = rpmMkdirPath(Lurlfn, "_icondir"); Lurlfn = _free(Lurlfn); if (rpmrc != RPMRC_OK) return -1; if (rpmIsVerbose() && !quietly && (rpmBTArgs.buildAmount & RPMBUILD_FETCHSOURCE)) rpmlog(RPMLOG_NOTICE, "Checking source and patch file(s):\n"); ec = 0; for (sp = spec->sources; sp != NULL; sp = sp->next) { #if defined(RPM_VENDOR_OPENPKG) /* splitted-source-directory */ Smacro = "%{?_specdir}/"; #endif #if defined(RPM_VENDOR_OPENPKG) /* splitted-source-directory */ if (! (Lmacro = getSourceDir(sp->flags, sp->source))) #else if (! (Lmacro = getSourceDir(sp->flags))) #endif continue; if (sp->flags & RPMFILE_SOURCE) { Rmacro = "%{?_Rsourcedir}/"; } else if (sp->flags & RPMFILE_PATCH) { Rmacro = "%{?_Rpatchdir}/"; } else if (sp->flags & RPMFILE_ICON) { Rmacro = "%{?_Ricondir}/"; } else continue; #if defined(RPM_VENDOR_OPENPKG) /* splitted-source-directory */ /* support splitted source directories, i.e., source files which are alternatively placed into the .spec directory and picked up from there, too. */ Lurlfn = rpmGenPath(NULL, Smacro, sp->source); rc = Lstat(Lurlfn, &st); if (rc == 0) { prepFetchVerbose(sp, &st); goto bottom; } #endif Lurlfn = rpmGenPath(NULL, Lmacro, sp->source); rc = Lstat(Lurlfn, &st); if (rc == 0) { /*@-noeffect@*/ prepFetchVerbose(sp, &st); /*@=noeffect@*/ goto bottom; } /*@-noeffect@*/ prepFetchVerbose(sp, NULL); /*@=noeffect@*/ if (errno != ENOENT) { ec++; rpmlog(RPMLOG_ERR, _("Missing %s%d %s: %s\n"), ((sp->flags & RPMFILE_SOURCE) ? "Source" : "Patch"), sp->num, sp->source, strerror(ENOENT)); goto bottom; } /* try to fetch via macro-controlled remote locations */ cp = rpmExpand(Rmacro, NULL); if (cp != NULL && strcmp(cp, "/") != 0) { cp = _free(cp); Rurlfn = rpmGenPath(NULL, Rmacro, sp->source); if (!(Rurlfn == NULL || Rurlfn[0] == '\0' || !strcmp(Rurlfn, "/") || !strcmp(Lurlfn, Rurlfn))) { rpmlog(RPMLOG_NOTICE, _("Fetching(%s%d): %s\n"), (sp->flags & RPMFILE_SOURCE) ? "Source" : "Patch", sp->num, Rurlfn); rc = urlGetFile(Rurlfn, Lurlfn); if (rc == 0) goto bottom; else { rpmlog(RPMLOG_ERR, _("Fetching %s%d failed: %s\n"), (sp->flags & RPMFILE_SOURCE) ? "Source" : "Patch", sp->num, ftpStrerror(rc)); ec++; } } } cp = _free(cp); /* try to fetch from original location */ rpmlog(RPMLOG_NOTICE, _("Fetching(%s%d): %s\n"), (sp->flags & RPMFILE_SOURCE) ? "Source" : "Patch", sp->num, sp->fullSource); rc = urlGetFile(sp->fullSource, Lurlfn); if (rc == 0) goto bottom; else { rpmlog(RPMLOG_ERR, _("Fetching %s%d failed: %s\n"), (sp->flags & RPMFILE_SOURCE) ? "Source" : "Patch", sp->num, ftpStrerror(rc)); ec++; } rpmlog(RPMLOG_ERR, _("Missing %s%d: %s: %s\n"), ((sp->flags & RPMFILE_SOURCE) ? "Source" : "Patch"), sp->num, sp->source, strerror(ENOENT)); ec++; bottom: Lurlfn = _free(Lurlfn); Rurlfn = _free(Rurlfn); } return ec; }