static unsigned char * get_auth_entry_text(struct listbox_item *item, struct terminal *term) { struct auth_entry *auth_entry = (struct auth_entry *)item->udata; return get_uri_string(auth_entry->uri, URI_HTTP_AUTH); }
static unsigned char * get_cache_entry_text(struct listbox_item *item, struct terminal *term) { struct cache_entry *cached = item->udata; return get_uri_string(cached->uri, URI_PUBLIC); }
static void fsp_directory(FSP_SESSION *ses, struct uri *uri) { struct string buf; FSP_DIR *dir; unsigned char *data = get_uri_string(uri, URI_DATA); unsigned char dircolor[8] = ""; if (!data) fsp_error(connection_state(S_OUT_OF_MEM)); decode_uri(data); if (!is_in_state(init_directory_listing(&buf, uri), S_OK)) fsp_error(connection_state(S_OUT_OF_MEM)); dir = fsp_opendir(ses, data); if (!dir) fsp_error(connection_state_for_errno(errno)); fprintf(stderr, "text/html"); fclose(stderr); puts(buf.source); if (get_opt_bool("document.browse.links.color_dirs", NULL)) { color_to_string(get_opt_color("document.colors.dirs", NULL), dircolor); } sort_and_display_entries(dir, dircolor); fsp_closedir(dir); puts("</pre><hr/></body></html>"); fsp_close_session(ses); exit(0); }
static void auth_ok(void *data) { struct dialog *dlg = (struct dialog *)data; struct auth_entry *entry = (struct auth_entry *)dlg->udata2; struct session *ses = (struct session *)dlg->udata; entry->blocked = 0; entry->valid = auth_entry_has_userinfo(entry); #ifdef CONFIG_FORMHIST if (get_opt_bool((const unsigned char *)"document.browse.forms.show_formhist", ses)) { unsigned char *url = get_uri_string(entry->uri, URI_HTTP_AUTH); if (url) { struct form form = {}; form.action = url; INIT_LIST_OF(struct submitted_value, submit); struct submitted_value *user, *password; user = init_submitted_value((unsigned char *)"user", entry->user, FC_TEXT, NULL, 0); if (user) { add_to_list(submit, user); } password = init_submitted_value((unsigned char *)"password", entry->password, FC_PASSWORD, NULL, 0); if (password) { add_to_list(submit, password); } memorize_form(ses, &submit, &form); done_submitted_value_list(&submit); mem_free(url); } } #endif if (entry->valid && have_location(ses)) { struct location *loc = cur_loc(ses); struct uri *uri = loc->vs.uri; /* Make a 'fake' redirect to a URI without user/password so that * the user/password from the URI will not override what the * user just entered in the dialog. */ if ((uri->userlen && strlcmp(entry->user, -1, uri->user, uri->userlen)) || (uri->password && strlcmp(entry->password, -1, uri->password, uri->passwordlen))) { uri = get_composed_uri(uri, URI_HTTP_AUTH | URI_DATA | URI_POST); if (uri) { goto_uri_frame(ses, uri, NULL, CACHE_MODE_INCREMENT); done_uri(uri); return; } } } reload(ses, CACHE_MODE_INCREMENT); }
void make_connection(struct socket *socket, struct uri *uri, socket_connect_T connect_done, int no_cache) { unsigned char *host = get_uri_string(uri, URI_DNS_HOST); struct connect_info *connect_info; enum dns_result result; enum blacklist_flags verify; socket->ops->set_timeout(socket, connection_state(0)); if (!host) { socket->ops->retry(socket, connection_state(S_OUT_OF_MEM)); return; } connect_info = init_connection_info(uri, socket, connect_done); if (!connect_info) { mem_free(host); socket->ops->retry(socket, connection_state(S_OUT_OF_MEM)); return; } socket->connect_info = connect_info; /* XXX: Keep here and not in init_connection_info() to make * complete_connect_socket() work from the HTTP implementation. */ socket->need_ssl = get_protocol_need_ssl(uri->protocol); if (!socket->set_no_tls) { enum blacklist_flags flags = get_blacklist_flags(uri); socket->no_tls = ((flags & SERVER_BLACKLIST_NO_TLS) != 0); socket->set_no_tls = 1; } verify = get_blacklist_flags(uri); socket->verify = ((verify & SERVER_BLACKLIST_NO_CERT_VERIFY) == 0); debug_transfer_log("\nCONNECTION: ", -1); debug_transfer_log(host, -1); debug_transfer_log("\n", -1); result = find_host(host, &connect_info->dnsquery, (dns_callback_T) dns_found, socket, no_cache); mem_free(host); if (result == DNS_ASYNC) socket->ops->set_state(socket, connection_state(S_DNS)); }
static unsigned char * get_file_download_text(struct listbox_item *item, struct terminal *term) { struct file_download *file_download = item->udata; unsigned char *uristring; uristring = get_uri_string(file_download->uri, URI_PUBLIC); if (uristring) { #ifdef CONFIG_UTF8 if (term->utf8_cp) decode_uri(uristring); else #endif /* CONFIG_UTF8 */ decode_uri_for_display(uristring); } return uristring; }
/* @location_class.getProperty */ static JSBool location_get_property(JSContext *ctx, JSObject *obj, jsid id, jsval *vp) { JSObject *parent_win; /* instance of @window_class */ struct view_state *vs; /* This can be called if @obj if not itself an instance of the * appropriate class but has one in its prototype chain. Fail * such calls. */ if (!JS_InstanceOf(ctx, obj, (JSClass *) &location_class, NULL)) return JS_FALSE; parent_win = JS_GetParent(ctx, obj); assert(JS_InstanceOf(ctx, parent_win, (JSClass *) &window_class, NULL)); if_assert_failed return JS_FALSE; vs = (struct view_state *)JS_GetInstancePrivate(ctx, parent_win, (JSClass *) &window_class, NULL); if (!JSID_IS_INT(id)) return JS_TRUE; undef_to_jsval(ctx, vp); switch (JSID_TO_INT(id)) { case JSP_LOC_HREF: astring_to_jsval(ctx, vp, get_uri_string(vs->uri, URI_ORIGINAL)); break; default: /* Unrecognized integer property ID; someone is using * the object as an array. SMJS builtin classes (e.g. * js_RegExpClass) just return JS_TRUE in this case * and leave *@vp unchanged. Do the same here. * (Actually not quite the same, as we already used * @undef_to_jsval.) */ break; } return JS_TRUE; }
static JSBool location_get_property_href(JSContext *ctx, JSHandleObject hobj, JSHandleId hid, JSMutableHandleValue hvp) { ELINKS_CAST_PROP_PARAMS JSObject *parent_win; /* instance of @window_class */ struct view_state *vs; /* This can be called if @obj if not itself an instance of the * appropriate class but has one in its prototype chain. Fail * such calls. */ if (!JS_InstanceOf(ctx, obj, &location_class, NULL)) return JS_FALSE; parent_win = JS_GetParent(obj); assert(JS_InstanceOf(ctx, parent_win, &window_class, NULL)); if_assert_failed return JS_FALSE; vs = JS_GetInstancePrivate(ctx, parent_win, &window_class, NULL); astring_to_jsval(ctx, vp, get_uri_string(vs->uri, URI_ORIGINAL)); return JS_TRUE; }
void download_data_store( struct download *download, struct file_download *file_download ) { struct terminal *term = file_download->term; assert_terminal_ptr_not_dangling( file_download->term ); if ( assert_failed ) { assert_failed = 0; file_download->term = 0; abort_download( &file_download[0] ); term = &term[0]; return; } else { if ( term[0].next == 0 ) { abort_download( &file_download[0] ); term = &term[0]; return; } else { if ( download->state.basic >= 0 ) { if ( file_download->dlg_data ) { redraw_dialog( file_download->dlg_data, 1 ); return; } else { return; } } else { if ( file_download->dlg_data != -100000 ) { unsigned char *url = get_uri_string( file_download->uri, URI_PUBLIC ); struct connection_state state = download->state; state.syserr = download->state.syserr; abort_download_and_beep( &file_download[0], &term[0] ); if ( url ) { info_box( &term[0], MSGBOX_FREE_TEXT, "Download error", ALIGN_CENTER ); mem_free( (void*)url ); return; } else { return; } } else { if ( file_download->external_handler ) { close( file_download->handle ); file_download->handle = -1; exec_on_terminal( &term[0], file_download->external_handler, file_download->file, ( (int)file_download->bits_at_104/*.1_1of4*/ >> 2 ) & 1 ); file_download->bits_at_104/*.1_1of4*/ &= 254; abort_download_and_beep( &file_download[0], &term[0] ); return; } else { if ( file_download->notify ) { unsigned char *url; url[0] = get_uri_string( file_download->uri, URI_PUBLIC ); done_download_display( &file_download[0] ); if ( url[0] ) { info_box( &term[0], MSGBOX_FREE_TEXT, "Download", ALIGN_CENTER ); mem_free( &url[0] ); } } if ( file_download->remotetime && *(int*)(get_opt_( config_options, (unsigned char*)config_options )) ) { struct utimbuf foo; foo.modtime = file_download->remotetime; foo.actime = file_download->remotetime; utime( (char*)file_download->file, &foo.actime ); } abort_download_and_beep( &file_download[0], &term[0] ); return; } } } }
void get_urls_css (struct map_context *ctx, int offset, int buf_length) { int token; /*char tmp[2048];*/ int buffer_pos = 0; int pos, length; char *uri; /* strncpy(tmp,ctx->text + offset, buf_length); tmp[buf_length] = '\0'; DEBUGP (("get_urls_css: \"%s\"\n", tmp)); */ /* tell flex to scan from this buffer */ yy_scan_bytes (ctx->text + offset, buf_length); while((token = yylex()) != CSSEOF) { /*DEBUGP (("%s ", token_names[token]));*/ /* @import "foo.css" or @import url(foo.css) */ if(token == IMPORT_SYM) { do { buffer_pos += yyleng; } while((token = yylex()) == S); /*DEBUGP (("%s ", token_names[token]));*/ if (token == STRING || token == URI) { /*DEBUGP (("Got URI "));*/ pos = buffer_pos + offset; length = yyleng; if (token == URI) { uri = get_uri_string (ctx->text, &pos, &length); } else { /* cut out quote characters */ pos++; length -= 2; uri = xmalloc (length + 1); strncpy (uri, yytext + 1, length); uri[length] = '\0'; } if (uri) { struct urlpos *up = append_url (uri, pos, length, ctx); DEBUGP (("Found @import: [%s] at %d [%s]\n", yytext, buffer_pos, uri)); if (up) { up->link_inline_p = 1; up->link_css_p = 1; up->link_expect_css = 1; } xfree(uri); } } } /* background-image: url(foo.png) note that we don't care what property this is actually on. */ else if(token == URI) { pos = buffer_pos + offset; length = yyleng; uri = get_uri_string (ctx->text, &pos, &length); if (uri) { struct urlpos *up = append_url (uri, pos, length, ctx); DEBUGP (("Found URI: [%s] at %d [%s]\n", yytext, buffer_pos, uri)); if (up) { up->link_inline_p = 1; up->link_css_p = 1; } xfree (uri); } } buffer_pos += yyleng; } DEBUGP (("\n")); }
void html_form(struct html_context *html_context, unsigned char *a, unsigned char *xxx3, unsigned char *xxx4, unsigned char **xxx5) { unsigned char *al; struct form *form; html_context->was_br = 1; form = init_form(); if (!form) return; form->method = FORM_METHOD_GET; form->form_num = a - html_context->startf; al = get_attr_val(a, (unsigned char *)"method", html_context->doc_cp); if (al) { if (!c_strcasecmp((const char *)al, "post")) { unsigned char *enctype; enctype = get_attr_val(a, (unsigned char *)"enctype", html_context->doc_cp); form->method = FORM_METHOD_POST; if (enctype) { if (!c_strcasecmp((const char *)enctype, "multipart/form-data")) form->method = FORM_METHOD_POST_MP; else if (!c_strcasecmp((const char *)enctype, "text/plain")) form->method = FORM_METHOD_POST_TEXT_PLAIN; mem_free(enctype); } } mem_free(al); } form->onsubmit = get_attr_val(a, (unsigned char *)"onsubmit", html_context->doc_cp); al = get_attr_val(a, (unsigned char *)"name", html_context->doc_cp); if (al) form->name = al; al = get_attr_val(a, (unsigned char *)"action", html_context->doc_cp); /* The HTML specification at * http://www.w3.org/TR/REC-html40/interact/forms.html#h-17.3 states * that the behavior of an empty action attribute should be undefined. * Mozilla handles action="" as action="<current-URI>" which seems * reasonable. (bug 615) */ if (al && *al) { form->action = join_urls(html_context->base_href, trim_chars(al, ' ', NULL)); mem_free(al); } else { enum uri_component components = URI_ORIGINAL; mem_free_if(al); /* We have to do following for GET method, because we would end * up with two '?' otherwise. */ if (form->method == FORM_METHOD_GET) components = URI_FORM_GET; form->action = get_uri_string(html_context->base_href, components); /* No action URI should contain post data */ assert(!form->action || !strchr((char *)form->action, POST_CHAR)); /* GET method URIs should not have '?'. */ assert(!form->action || form->method != FORM_METHOD_GET || !strchr((char *)form->action, '?')); } al = get_target(html_context->options, a); form->target = al ? al : stracpy(html_context->base_target); html_context->special_f(html_context, SP_FORM, form); }
void do_auth_dialog( struct session *ses, void *data ) { int edx; struct dialog *dlg; struct dialog_data *dlg_data; struct terminal *term = ses->tab->term; struct auth_entry *a; unsigned char sticker[1024], *text; int sticker_len; if ( get_invalid_auth_entry( ) && !( ( *(char*)(get_invalid_auth_entry( ) + 112) & 1 ) & 255 ) && get_uri_string( &a->uri->user[0], URI_HTTP_AUTH ) ) { dlg = &a->realm[0]; if ( 0 != 65 ) { if ( term[0] && current_charset != get_terminal_codepage( &term[0] ) ) { bind_textdomain_codeset( "elinks", get_cp_mime_name( get_terminal_codepage( &term[0] ) ) ); *ebp_1076 = text[0]; current_charset = get_terminal_codepage( &term[0] ); } gettext( "Authentication required for %s at %s" ); text[0] = text[0]; } sticker_len = __snprintf_chk( sticker, 1024, 1, 1024, (char*)gettext( "Authentication required for %s at %s" ), dlg, text[0] ); mem_free( &text[0] ); if ( sticker_len <= 1024 ) { dlg = (struct dialog*)mem_calloc( 1, sticker_len + 277 ); if ( dlg ) { a->bits_at_112/*.1_1of4*/ |= 1; dlg->layouter = &generic_dialog_layouter; dlg[0].title = _( "Authentication required", &term[0] ); text[0] = dlg[3].abort; memcpy( dlg[3].abort, sticker, sticker_len ); dlg->udata = (void*)ses[0].next; dlg->udata2 = (void*)a[0].next; add_dlg_text( dlg, (unsigned char*)dlg[3].abort, ALIGN_LEFT, 0 ); add_dlg_field_do( dlg, WIDGET_FIELD, _( "Login", &term[0] ), 0, 0, 0, 40, &a->user[0], 0, INPFIELD_FLOAT ); add_dlg_field_do( dlg, WIDGET_FIELD_PASS, _( "Password", &term[0] ), 0, 0, 0, 40, &a->password[0], 0, INPFIELD_FLOAT ); add_dlg_button_do( dlg, _( "~OK", &term[0] ), 1, &ok_dialog, 0, &auth_ok, (void*)dlg ); add_dlg_button_do( dlg, _( "~Cancel", &term[0] ), 2, &ok_dialog, 0, &auth_cancel, (void*)a[0].next ); if ( assert_failed == 0 ) { assert_failed = dlg->number_of_widgets != 5; if ( dlg->number_of_widgets != 5 ) { errfile = "/home/naftali/source/elinks-0.12~pre5/src/protocol/auth/dialogs.c"; errline = 118; elinks_internal( "assertion 5 == (dlg)->number_of_widgets failed!" ); } } if ( dlg_data[0] && a->user[0] && a->password[0] == 0 ) select_widget_by_id( &dlg_data[0], 1 ); } } } if ( 0 ^ 0 ) { __stack_chk_fail( ); } return; }
void mailcap_protocol_handler(struct connection *conn) { #ifdef HAVE_FORK unsigned char *script, *ref; pid_t pid; struct connection_state state = connection_state(S_OK); int pipe_read[2], check; /* security checks */ if (!conn->referrer || conn->referrer->protocol != PROTOCOL_MAILCAP) { goto bad; } ref = get_uri_string(conn->referrer, URI_DATA); if (!ref) { goto bad; } check = strcmp(ref, "elmailcap"); mem_free(ref); if (check) goto bad; script = get_uri_string(conn->uri, URI_DATA); if (!script) { state = connection_state(S_OUT_OF_MEM); goto end2; } if (c_pipe(pipe_read)) { state = connection_state_for_errno(errno); goto end1; } pid = fork(); if (pid < 0) { state = connection_state_for_errno(errno); goto end0; } if (!pid) { if (dup2(pipe_read[1], STDOUT_FILENO) < 0) { _exit(2); } /* We implicitly chain stderr to ELinks' stderr. */ close_all_non_term_fd(); if (execl("/bin/sh", "/bin/sh", "-c", script, (char *) NULL)) { _exit(3); } } else { /* ELinks */ mem_free(script); if (!init_http_connection_info(conn, 1, 0, 1)) { close(pipe_read[0]); close(pipe_read[1]); return; } close(pipe_read[1]); conn->socket->fd = pipe_read[0]; conn->data_socket->fd = -1; conn->cgi = 1; set_nonblocking_fd(conn->socket->fd); get_request(conn); return; } end0: close(pipe_read[0]); close(pipe_read[1]); end1: mem_free(script); end2: abort_connection(conn, state); return; #endif bad: abort_connection(conn, connection_state(S_BAD_URL)); }
static void do_smb(struct connection *conn) { struct uri *uri = conn->uri; struct auth_entry *auth = find_auth(uri); struct string string; unsigned char *url; int dir; if ((uri->userlen && uri->passwordlen) || !auth) { url = get_uri_string(uri, URI_BASE); } else { unsigned char *uri_string = get_uri_string(uri, URI_HOST | URI_PORT | URI_DATA); if (!uri_string || !init_string(&string)) { smb_error(connection_state(S_OUT_OF_MEM)); } /* Must URI-encode the username and password to avoid * ambiguity if they contain "/:@" characters. * Libsmbclient then decodes them again, and the * server gets them as they were in auth->user and * auth->password, i.e. as the user typed them in the * auth dialog. This implies that, if the username or * password contains some characters or bytes that the * user cannot directly type, then she cannot enter * them. If that becomes an actual problem, it should * be fixed in the auth dialog, e.g. by providing a * hexadecimal input mode. */ add_to_string(&string, "smb://"); encode_uri_string(&string, auth->user, -1, 1); add_char_to_string(&string, ':'); encode_uri_string(&string, auth->password, -1, 1); add_char_to_string(&string, '@'); add_to_string(&string, uri_string); url = string.source; } if (!url) { smb_error(connection_state(S_OUT_OF_MEM)); } if (smbc_init(smb_auth, 0)) { smb_error(connection_state_for_errno(errno)); }; dir = smbc_opendir(url); if (dir >= 0) { struct string prefix; init_string(&prefix); add_to_string(&prefix, url); add_char_to_string(&prefix, '/'); smb_directory(dir, &prefix, conn->uri); done_string(&prefix); } else { const int errno_from_opendir = errno; char buf[READ_SIZE]; struct stat sb; int r, res, fdout; int file = smbc_open(url, O_RDONLY, 0); if (file < 0) { /* If we're opening the list of shares without * proper authentication, then smbc_opendir * fails with EACCES and smbc_open fails with * ENOENT. In this case, return the EACCES so * that the parent ELinks process will prompt * for credentials. */ if (errno == ENOENT && errno_from_opendir == EACCES) errno = errno_from_opendir; smb_error(connection_state_for_errno(errno)); } res = smbc_fstat(file, &sb); if (res) { smb_error(connection_state_for_errno(res)); } /* filesize */ fprintf(header_out, "%" OFF_PRINT_FORMAT, (off_print_T) sb.st_size); fclose(header_out); fdout = fileno(data_out); while ((r = smbc_read(file, buf, READ_SIZE)) > 0) { if (safe_write(fdout, buf, r) <= 0) break; } smbc_close(file); exit(0); } }
void ses_goto(struct session *ses, struct uri *uri, unsigned char *target_frame, struct location *target_location, enum cache_mode cache_mode, enum task_type task_type, int redir) { /* [gettext_accelerator_context(ses_goto)] */ struct task *task; int referrer_incomplete = 0; int malicious_uri = 0; int confirm_submit = uri->form && get_opt_bool("document.browse.forms" ".confirm_submit", ses); unsigned char *m1 = NULL, *message = NULL; struct memory_list *mlist = NULL; if (ses->doc_view && ses->doc_view->document && ses->doc_view->document->refresh) { kill_document_refresh(ses->doc_view->document->refresh); } assertm(!ses->loading_uri, "Buggy URI reference counting"); /* Reset the redirect counter if this is not a redirect. */ if (!redir) { ses->redirect_cnt = 0; } /* Figure out whether to confirm submit or not */ /* Only confirm submit if we are posting form data or a misleading URI * was detected. */ /* Note uri->post might be empty here but we are still supposely * posting form data so this should be more correct. */ if (uri->user && uri->userlen && get_opt_bool("document.browse.links.warn_malicious", ses) && check_malicious_uri(uri)) { malicious_uri = 1; confirm_submit = 1; } else if (uri->form) { /* First check if the referring URI was incomplete. It * indicates that the posted form data might be incomplete too. * See bug 460. */ if (ses->referrer) { struct cache_entry *cached; cached = find_in_cache(ses->referrer); referrer_incomplete = (cached && cached->incomplete); } if (referrer_incomplete) { confirm_submit = 1; } else if (get_validated_cache_entry(uri, cache_mode)) { confirm_submit = 0; } } if (!confirm_submit) { ses_load(ses, get_uri_reference(uri), target_frame, target_location, cache_mode, task_type); return; } task = mem_alloc(sizeof(*task)); if (!task) return; task->ses = ses; task->uri = get_uri_reference(uri); task->cache_mode = cache_mode; task->session_task.type = task_type; task->session_task.target.frame = null_or_stracpy(target_frame); task->session_task.target.location = target_location; if (malicious_uri) { unsigned char *host = memacpy(uri->host, uri->hostlen); unsigned char *user = memacpy(uri->user, uri->userlen); unsigned char *uristring = get_uri_string(uri, URI_PUBLIC); message = msg_text(ses->tab->term, N_("The URL you are about to follow might be maliciously " "crafted in order to confuse you. By following the URL " "you will be connecting to host \"%s\" as user \"%s\".\n\n" "Do you want to go to URL %s?"), host, user, uristring); mem_free_if(host); mem_free_if(user); mem_free_if(uristring); } else if (redir) { m1 = N_("Do you want to follow the redirect and post form data " "to URL %s?"); } else if (referrer_incomplete) { m1 = N_("The form data you are about to post might be incomplete.\n" "Do you want to post to URL %s?"); } else if (task_type == TASK_FORWARD) { m1 = N_("Do you want to post form data to URL %s?"); } else { m1 = N_("Do you want to repost form data to URL %s?"); } if (!message && m1) { unsigned char *uristring = get_uri_string(uri, URI_PUBLIC); message = msg_text(ses->tab->term, m1, uristring); mem_free_if(uristring); } add_to_ml(&mlist, task, (void *) NULL); if (task->session_task.target.frame) add_to_ml(&mlist, task->session_task.target.frame, (void *) NULL); msg_box(ses->tab->term, mlist, MSGBOX_FREE_TEXT, N_("Warning"), ALIGN_CENTER, message, task, 2, MSG_BOX_BUTTON(N_("~Yes"), post_yes, B_ENTER), MSG_BOX_BUTTON(N_("~No"), post_no, B_ESC)); }
/* Return -1 on error, 0 or success. */ int ssl_connect(struct socket *socket) { int ret; unsigned char *server_name; struct connection *conn = (struct connection *)socket->conn; /* TODO: Recode server_name to UTF-8. */ server_name = get_uri_string(conn->proxied_uri, URI_HOST); if (!server_name) { socket->ops->done(socket, connection_state(S_OUT_OF_MEM)); return -1; } /* RFC 3546 says literal IPv4 and IPv6 addresses are not allowed. */ if (is_ip_address(server_name, strlen((const char *)server_name))) mem_free_set(&server_name, NULL); if (init_ssl_connection(socket, server_name) == S_SSL_ERROR) { mem_free_if(server_name); socket->ops->done(socket, connection_state(S_SSL_ERROR)); return -1; } mem_free_if(server_name); if (socket->no_tls) ssl_set_no_tls(socket); #ifdef USE_OPENSSL SSL_set_fd((SSL *)socket->ssl, socket->fd); if (get_opt_bool((const unsigned char *)"connection.ssl.cert_verify", NULL)) SSL_set_verify((SSL *)socket->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); if (get_opt_bool((const unsigned char *)"connection.ssl.client_cert.enable", NULL)) { unsigned char *client_cert; #ifdef CONFIG_NSS_COMPAT_OSSL client_cert = get_opt_str( (const unsigned char *)"connection.ssl.client_cert.nickname", NULL); #else client_cert = get_opt_str( (const unsigned char *)"connection.ssl.client_cert.file", NULL); #endif if (!*client_cert) { client_cert = (unsigned char *)getenv("X509_CLIENT_CERT"); if (client_cert && !*client_cert) client_cert = NULL; } if (client_cert) { #ifdef CONFIG_NSS_COMPAT_OSSL SSL_CTX_use_certificate_chain_file( (SSL *) socket->ssl, (const char *)client_cert); #else SSL_CTX *ctx = ((SSL *) socket->ssl)->ctx; SSL_CTX_use_certificate_chain_file(ctx, (const char *)client_cert); SSL_CTX_use_PrivateKey_file(ctx, (const char *)client_cert, SSL_FILETYPE_PEM); #endif } } #elif defined(CONFIG_GNUTLS) /* GnuTLS uses function pointers for network I/O. The default * functions take a file descriptor, but it must be passed in * as a pointer. GnuTLS uses the GNUTLS_INT_TO_POINTER and * GNUTLS_POINTER_TO_INT macros for these conversions, but * those are unfortunately not in any public header. So * ELinks must just cast the pointer the best it can and hope * that the conversions match. */ gnutls_transport_set_ptr(*((ssl_t *) socket->ssl), (gnutls_transport_ptr_t) (longptr_T) socket->fd); /* TODO: Some certificates fuss. --pasky */ #endif ret = ssl_do_connect(socket); switch (ret) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ2: socket->ops->set_state(socket, connection_state(S_SSL_NEG)); set_handlers(socket->fd, (select_handler_T) ssl_want_read, NULL, (select_handler_T) dns_exception, socket); return -1; case SSL_ERROR_NONE: #ifdef CONFIG_GNUTLS if (!get_opt_bool((const unsigned char *)"connection.ssl.cert_verify", NULL)) break; if (!verify_certificates(socket)) #endif break; default: if (ret != SSL_ERROR_NONE) { /* DBG("sslerr %s", gnutls_strerror(ret)); */ socket->no_tls = !socket->no_tls; } connect_socket(socket, connection_state(S_SSL_ERROR)); return -1; } return 0; }
/** Verify one certificate in the server certificate chain. * This callback is documented in SSL_set_verify(3). */ static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { X509 *cert; SSL *ssl; struct socket *socket; struct connection *conn; unsigned char *host_in_uri; GENERAL_NAMES *alts; int saw_dns_name = 0; int matched = 0; /* If OpenSSL already found a problem, keep that. */ if (!preverify_ok) return 0; /* Examine only the server certificate, not CA certificates. */ if (X509_STORE_CTX_get_error_depth(ctx) != 0) return preverify_ok; cert = X509_STORE_CTX_get_current_cert(ctx); ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); socket = (struct socket *)SSL_get_ex_data(ssl, socket_SSL_ex_data_idx); conn = (struct connection *)socket->conn; host_in_uri = get_uri_string(conn->uri, URI_HOST | URI_IDN); if (!host_in_uri) return 0; /* RFC 5280 section 4.2.1.6 describes the subjectAltName extension. * RFC 2818 section 3.1 says Common Name must not be used * if dNSName is present. */ alts = (GENERAL_NAMES *)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (alts != NULL) { int alt_count; int alt_pos; GENERAL_NAME *alt; alt_count = sk_GENERAL_NAME_num(alts); for (alt_pos = 0; !matched && alt_pos < alt_count; ++alt_pos) { alt = sk_GENERAL_NAME_value(alts, alt_pos); if (alt->type == GEN_DNS) { saw_dns_name = 1; matched = match_uri_host_name(host_in_uri, alt->d.dNSName); } else if (alt->type == GEN_IPADD) { matched = match_uri_host_ip(host_in_uri, alt->d.iPAddress); } } /* Free the GENERAL_NAMES list and each element. */ sk_GENERAL_NAME_pop_free(alts, GENERAL_NAME_free); } if (!matched && !saw_dns_name) { X509_NAME *name; int cn_index; X509_NAME_ENTRY *entry = NULL; name = X509_get_subject_name(cert); cn_index = X509_NAME_get_index_by_NID(name, NID_commonName, -1); if (cn_index >= 0) entry = X509_NAME_get_entry(name, cn_index); if (entry != NULL) matched = match_uri_host_name(host_in_uri, X509_NAME_ENTRY_get_data(entry)); } mem_free(host_in_uri); return matched; }
static int verify_certificates(struct socket *socket) { gnutls_x509_crt_t cert; gnutls_session_t session = *(ssl_t *)socket->ssl; struct connection *conn = socket->conn; const gnutls_datum_t *cert_list; unsigned char *hostname; int ret; unsigned int cert_list_size, status; ret = gnutls_certificate_verify_peers2(session, &status); if (ret) return ret; if (status) return status; /* If the certificate is of a type for which verification has * not yet been implemented, then reject it. This way, a fake * server cannot avoid verification by using a strange type of * certificate. * * OpenPGP certificates shouldn't even get this far anyway, * because init_ssl_connection() tells GnuTLS to disable * OpenPGP, and ELinks never calls * gnutls_certificate_set_openpgp_keyring_file, so status * should have been GNUTLS_CERT_SIGNER_NOT_FOUND. */ if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) return -7; if (gnutls_x509_crt_init(&cert) < 0) { return -1; } cert_list = gnutls_certificate_get_peers(session, &cert_list_size); if (!cert_list) { return -2; } if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) { return -3; } if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL)) { gnutls_x509_crt_deinit(cert); return -4; } if (gnutls_x509_crt_get_activation_time(cert) > time(NULL)) { gnutls_x509_crt_deinit(cert); return -5; } /* Because RFC 5280 defines dNSName as an IA5String, it can * only contain ASCII characters. Internationalized domain * names must thus be in Punycode form. Because GnuTLS 2.8.6 * does not itself support IDN, ELinks must convert. */ hostname = get_uri_string(conn->uri, URI_HOST | URI_IDN); if (!hostname) return -6; ret = !gnutls_x509_crt_check_hostname(cert, hostname); gnutls_x509_crt_deinit(cert); mem_free(hostname); return ret; }
static void do_fsp(struct connection *conn) { FSP_SESSION *ses; struct stat sb; struct uri *uri = conn->uri; struct auth_entry *auth; unsigned char *host = get_uri_string(uri, URI_HOST); unsigned char *data = get_uri_string(uri, URI_DATA); unsigned short port = (unsigned short)get_uri_port(uri); unsigned char *password = NULL; decode_uri(data); if (uri->passwordlen) { password = get_uri_string(uri, URI_PASSWORD); } else { auth = find_auth(uri); if (auth) password = auth->password; } /* fsp_open_session may not set errno if getaddrinfo fails * https://sourceforge.net/tracker/index.php?func=detail&aid=2036798&group_id=93841&atid=605738 * Try to detect this bug and use an ELinks-specific error * code instead, so that we can display a message anyway. */ errno = 0; ses = fsp_open_session(host, port, password); if (!ses) { if (errno) fsp_error(connection_state_for_errno(errno)); else fsp_error(connection_state(S_FSP_OPEN_SESSION_UNKN)); } /* fsplib 0.8 ABI depends on _FILE_OFFSET_BITS * https://sourceforge.net/tracker/index.php?func=detail&aid=1674729&group_id=93841&atid=605738 * If ELinks and fsplib are using different values of * _FILE_OFFSET_BITS, then they get different definitions of * struct stat, and the st_size stored by fsp_stat is * typically not the same as the st_size read by ELinks. * Fortunately, st_mode seems to have the same offset and size * in both versions of struct stat. * * If all the bytes used by the 32-bit st_size are also used * by the 64-bit st_size, then ELinks may be able to guess * which ones they are, because the current version 2 of FSP * supports only 32-bit file sizes in protocol packets. Begin * by filling struct stat with 0xAA so that it's easier to * detect which bytes fsp_stat has left unchanged. (Only * sb.st_size really needs to be filled, but filling the rest * too helps viewing the data with a debugger.) */ memset(&sb, 0xAA, sizeof(sb)); if (fsp_stat(ses, data, &sb)) fsp_error(connection_state_for_errno(errno)); if (S_ISDIR(sb.st_mode)) { fsp_directory(ses, uri); } else { /* regular file */ char buf[READ_SIZE]; FSP_FILE *file = fsp_fopen(ses, data, "r"); int r; if (!file) { fsp_error(connection_state_for_errno(errno)); } #if SIZEOF_OFF_T >= 8 if (sb.st_size < 0 || sb.st_size > 0xFFFFFFFF) { /* Probably a _FILE_OFFSET_BITS mismatch as * described above. Try to detect which half * of st_size is the real size. This may * depend on the endianness of the processor * and on the padding in struct stat. */ if ((sb.st_size & 0xFFFFFFFF00000000ULL) == 0xAAAAAAAA00000000ULL) sb.st_size = sb.st_size & 0xFFFFFFFF; else if ((sb.st_size & 0xFFFFFFFF) == 0xAAAAAAAA) sb.st_size = (sb.st_size >> 32) & 0xFFFFFFFF; else /* Can't figure it out. */ sb.st_size = 1; } #endif /* Send filesize */ fprintf(stderr, "%" OFF_PRINT_FORMAT "\n", (off_print_T) sb.st_size); fclose(stderr); while ((r = fsp_fread(buf, 1, READ_SIZE, file)) > 0) { int off = 0; while (r) { int w = safe_write(STDOUT_FILENO, buf + off, r); if (w == -1) goto out; off += w; r -= w; } } out: fsp_fclose(file); fsp_close_session(ses); exit(0); }
static void download_dialog_layouter(struct dialog_data *dlg_data) { struct file_download *file_download = dlg_data->dlg->udata; struct terminal *term = dlg_data->win->term; int w = dialog_max_width(term); int rw = w; int x, y = 0; int url_len; unsigned char *url; struct download *download = &file_download->download; struct color_pair *dialog_text_color = get_bfu_color(term, "dialog.text"); unsigned char *msg = get_download_msg(download, term, 1, 1, "\n"); int show_meter = (download_is_progressing(download) && download->progress->size >= 0); #if CONFIG_BITTORRENT int bittorrent = (file_download->uri->protocol == PROTOCOL_BITTORRENT && (show_meter || is_in_state(download->state, S_RESUME))); #endif redraw_windows(REDRAW_BEHIND_WINDOW, dlg_data->win); file_download->dlg_data = dlg_data; if (!msg) return; url = get_uri_string(file_download->uri, URI_PUBLIC); if (!url) { mem_free(msg); return; } #ifdef CONFIG_UTF8 if (term->utf8_cp) decode_uri(url); else #endif /* CONFIG_UTF8 */ decode_uri_for_display(url); url_len = strlen(url); if (show_meter) { int_lower_bound(&w, DOWN_DLG_MIN); } dlg_format_text_do(dlg_data, url, 0, &y, w, &rw, dialog_text_color, ALIGN_LEFT, 1); y++; if (show_meter) y += 2; #if CONFIG_BITTORRENT if (bittorrent) y += 2; #endif dlg_format_text_do(dlg_data, msg, 0, &y, w, &rw, dialog_text_color, ALIGN_LEFT, 1); y++; dlg_format_buttons(dlg_data, dlg_data->widgets_data, dlg_data->number_of_widgets, 0, &y, w, &rw, ALIGN_CENTER, 1); draw_dialog(dlg_data, w, y); w = rw; if (url_len > w) { /* Truncate too long urls */ url_len = w; url[url_len] = '\0'; if (url_len > 4) { url[--url_len] = '.'; url[--url_len] = '.'; url[--url_len] = '.'; } } y = dlg_data->box.y + DIALOG_TB + 1; x = dlg_data->box.x + DIALOG_LB; dlg_format_text_do(dlg_data, url, x, &y, w, NULL, dialog_text_color, ALIGN_LEFT, 0); if (show_meter) { y++; draw_progress_bar(download->progress, term, x, y, w, NULL, NULL); y++; } #if CONFIG_BITTORRENT if (bittorrent) { y++; draw_bittorrent_piece_progress(download, term, x, y, w, NULL, NULL); y++; } #endif y++; dlg_format_text_do(dlg_data, msg, x, &y, w, NULL, dialog_text_color, ALIGN_LEFT, 0); y++; dlg_format_buttons(dlg_data, dlg_data->widgets_data, dlg_data->number_of_widgets, x, &y, w, NULL, ALIGN_CENTER, 0); mem_free(url); mem_free(msg); }
/* TODO: Take auth_entry from data. --jonas */ void do_auth_dialog(struct session *ses, void *data) { /* [gettext_accelerator_context(do_auth_dialog)] */ struct dialog *dlg; struct dialog_data *dlg_data; struct terminal *term = ses->tab->term; struct auth_entry *a = get_invalid_auth_entry(); unsigned char sticker[MAX_STR_LEN], *text; int sticker_len; if (!a || a->blocked) return; text = get_uri_string(a->uri, URI_HTTP_AUTH); if (!text) return; #ifdef CONFIG_FORMHIST { unsigned char *user = get_form_history_value(text, (unsigned char *)"user"); unsigned char *password = get_form_history_value(text, (unsigned char *)"password"); if (user) { strncpy((char *)a->user, (const char *)user, AUTH_USER_MAXLEN - 1); } if (password) { strncpy((char *)a->password, (const char *)password, AUTH_PASSWORD_MAXLEN - 1); } } #endif sticker_len = snprintf((char *)sticker, sizeof(sticker), (const char *)_("Authentication required for %s at %s", term), a->realm, text); mem_free(text); if (sticker_len < 0 || sticker_len > MAX_STR_LEN) return; #define AUTH_WIDGETS_COUNT 5 /* + 1 to leave room for the '\0'. */ dlg = calloc_dialog(AUTH_WIDGETS_COUNT, sticker_len + 1); if (!dlg) return; a->blocked = 1; /* This function is used for at least HTTP and FTP, so don't * name the protocol here. Consider also what an FTP server * behind an HTTP proxy should be called. */ dlg->title = _("Authentication required", term); dlg->layouter = generic_dialog_layouter; text = get_dialog_offset(dlg, AUTH_WIDGETS_COUNT); memcpy(text, sticker, sticker_len); /* calloc_dialog has stored '\0' */ dlg->udata = (void *) ses; dlg->udata2 = a; add_dlg_text(dlg, text, ALIGN_LEFT, 0); add_dlg_field_float(dlg, _("Login", term), 0, 0, NULL, AUTH_USER_MAXLEN, a->user, NULL); add_dlg_field_float_pass(dlg, _("Password", term), 0, 0, NULL, AUTH_PASSWORD_MAXLEN, a->password); add_dlg_ok_button(dlg, _("~OK", term), B_ENTER, auth_ok, dlg); add_dlg_ok_button(dlg, _("~Cancel", term), B_ESC, auth_cancel, a); add_dlg_end(dlg, AUTH_WIDGETS_COUNT); dlg_data = do_dialog(term, dlg, getml(dlg, (void *) NULL)); /* When there's some username, but no password, automagically jump to * the password. */ if (dlg_data && a->user[0] && !a->password[0]) select_widget_by_id(dlg_data, 1); }