HostTxtEntry: Fixups for use by i2ptunnel

i2ptunnel: Add new registration authentication page
- Remove old, unused hostname signature generation
PrivateKeyFile: Ensure initialization before returning private keys
This commit is contained in:
zzz
2016-04-24 18:10:10 +00:00
parent 799d90e1b5
commit 2a34d1c44a
6 changed files with 271 additions and 9 deletions

View File

@ -71,6 +71,7 @@ public class EditBean extends IndexBean {
return _helper.getPrivateKeyFile(tunnel);
}
/****
public String getNameSignature(int tunnel) {
String spoof = getSpoofedHost(tunnel);
if (spoof.length() <= 0)
@ -100,6 +101,26 @@ public class EditBean extends IndexBean {
}
return "";
}
****/
/**
* @since 0.9.26
* @return key or null
*/
public SigningPrivateKey getSigningPrivateKey(int tunnel) {
TunnelController tun = getController(tunnel);
if (tun == null)
return null;
String keyFile = tun.getPrivKeyFile();
if (keyFile != null && keyFile.trim().length() > 0) {
File f = new File(keyFile);
if (!f.isAbsolute())
f = new File(_context.getConfigDir(), keyFile);
PrivateKeyFile pkf = new PrivateKeyFile(f);
return pkf.getSigningPrivKey();
}
return null;
}
public boolean startAutomatically(int tunnel) {
return _helper.shouldStartAutomatically(tunnel);

View File

@ -228,6 +228,9 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
<textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Read Only: Local Destination (if known)" wrap="off" spellcheck="false"><%=editBean.getDestinationBase64(curTunnel)%></textarea>
</div>
<%
/******
%>
<% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
String sig = editBean.getNameSignature(curTunnel);
if (sig.length() > 0) {
@ -240,6 +243,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
<%
} // sig
} // type
****/
String b64 = editBean.getDestinationBase64(curTunnel);
if (!"".equals(b64)) {
@ -256,11 +260,14 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
<a class="control" title="<%=intl._t("Generate QR Code")%>" href="/imagegen/qr?s=320&amp;t=<%=name%>&amp;c=http%3a%2f%2f<%=name%>%2f%3fi2paddresshelper%3d<%=b64%>" target="_top"><%=intl._t("Generate QR Code")%></a>
</label>
<a class="control" href="/susidns/addressbook.jsp?book=private&amp;hostname=<%=name%>&amp;destination=<%=b64%>#add"><%=intl._t("Add to local addressbook")%></a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a class="control" href="register?tunnel=<%=curTunnel%>"><%=intl._t("Registration Authentication")%></a>
<%
} else {
%>
<label> </label>
<span class="comment"><%=intl._t("Set name with .i2p suffix to enable QR code generation")%></span>
<span class="comment"><%=intl._t("Set name with .i2p suffix to enable registration authentication")%></span>
<%
} // name
%>

View File

@ -0,0 +1,191 @@
<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.EditBean,net.i2p.client.naming.HostTxtEntry,net.i2p.data.SigningPrivateKey,net.i2p.util.OrderedProperties"
%><%@page trimDirectiveWhitespaces="true"
%><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<%
/* right now using EditBean instead of IndexBean for getSpoofedHost() */
/* but might want to POST to it anyway ??? */
%>
<jsp:useBean class="net.i2p.i2ptunnel.web.EditBean" id="editBean" scope="request" />
<jsp:useBean class="net.i2p.i2ptunnel.web.Messages" id="intl" scope="request" />
<% String tun = request.getParameter("tunnel");
int curTunnel = -1;
if (tun != null) {
try {
curTunnel = Integer.parseInt(tun);
} catch (NumberFormatException nfe) {
curTunnel = -1;
}
}
%>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><%=intl._t("Hidden Services Manager")%> - <%=intl._t("Registration Helper")%></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
<link href="/themes/console/images/favicon.ico" type="image/x-icon" rel="shortcut icon" />
<% if (editBean.allowCSS()) {
%><link rel="icon" href="<%=editBean.getTheme()%>images/favicon.ico" />
<link href="<%=editBean.getTheme()%>default.css" rel="stylesheet" type="text/css" />
<link href="<%=editBean.getTheme()%>i2ptunnel.css" rel="stylesheet" type="text/css" />
<% }
%>
<style type='text/css'>
input.default { width: 1px; height: 1px; visibility: hidden; }
</style>
</head>
<body id="tunnelEditPage">
<div id="pageHeader">
</div>
<%
if (editBean.isInitialized()) {
%>
<form method="post" action="authenticate">
<div id="tunnelEditPanel" class="panel">
<div class="header">
<%
String tunnelTypeName;
String tunnelType;
boolean valid = false;
if (curTunnel >= 0) {
tunnelTypeName = editBean.getTunnelType(curTunnel);
tunnelType = editBean.getInternalType(curTunnel);
%><h4><%=intl._t("Registration Helper")%></h4><%
} else {
tunnelTypeName = "new";
tunnelType = "new";
%><h4>Fail</h4><p>Tunnel not found</p><%
}
String b64 = editBean.getDestinationBase64(curTunnel);
String name = editBean.getSpoofedHost(curTunnel);
if (name == null || name.equals(""))
name = editBean.getTunnelName(curTunnel);
%>
<input type="hidden" name="tunnel" value="<%=curTunnel%>" />
<input type="hidden" name="nonce" value="<%=net.i2p.i2ptunnel.web.IndexBean.getNextNonce()%>" />
<input type="hidden" name="type" value="<%=tunnelType%>" />
<input type="submit" class="default" name="action" value="Save changes" />
</div>
<%
if (!"new".equals(tunnelType)) {
%>
<div class="separator">
<hr />
</div>
<div id="nameField" class="rowItem">
<label for="name" accesskey="N">
<%=intl._t("Name")%>(<span class="accessKey">N</span>):
</label>
<span class="text"><%=editBean.getTunnelName(curTunnel)%></span>
</div>
<div id="typeField" class="rowItem">
<label><%=intl._t("Type")%>:</label>
<span class="text"><%=tunnelTypeName%></span>
</div>
<%
if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
%><div id="websiteField" class="rowItem">
<label for="spoofedHost" accesskey="W">
<%=intl._t("Website name")%>(<span class="accessKey">W</span>):
</label>
<span class="text"><%=editBean.getSpoofedHost(curTunnel)%></span>
</div>
<%
}
%>
<div id="destinationField" class="rowItem">
<label for="localDestination" accesskey="L">
<%=intl._t("Local destination")%>(<span class="accessKey">L</span>):
</label>
<textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Read Only: Local Destination (if known)" wrap="off" spellcheck="false"><%=editBean.getDestinationBase64(curTunnel)%></textarea>
</div>
<div class="subdivider">
<hr />
</div>
<%
if (b64 == null || b64.length() < 516) {
%><%=intl._t("Local destination is not available. Start the tunnel.")%><%
} else if (name == null || name.equals("") || name.contains(" ") || !name.endsWith(".i2p")) {
if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
%><%=intl._t("To enable registration verification, edit tunnel and set name (or website name) to a valid host name ending in '.i2p'")%><%
} else {
%><%=intl._t("To enable registration verification, edit tunnel and set name to a valid host name ending in '.i2p'")%><%
}
} else {
SigningPrivateKey spk = editBean.getSigningPrivateKey(curTunnel);
if (spk == null) {
%><%=intl._t("Destination signing key is not available. Start the tunnel.")%><%
} else {
valid = true;
OrderedProperties props = new OrderedProperties();
HostTxtEntry he = new HostTxtEntry(name, b64, props);
he.sign(spk);
%><div id="destinationField" class="rowItem">
<label><%=intl._t("Authentication Strings")%>:</label>
<span class="text"><%=intl._t("Select and copy the entire contents of the appropriate box")%></span>
</div>
<div id="sigField" class="rowItem">
<label for="signature">
<%=intl._t("Authentication for adding host")%>
</label>
<textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeProps(out); %></textarea>
</div>
<%
props.remove(HostTxtEntry.PROP_SIG);
props.setProperty(HostTxtEntry.PROP_ACTION, HostTxtEntry.ACTION_REMOVE);
he.signRemove(spk);
%><div id="sigField" class="rowItem">
<label for="signature">
<%=intl._t("Authentication for removing host")%>
</label>
<textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeRemove(out); %></textarea>
</div>
<div class="footer">
</div>
<%
} // spk != null
} // valid b64 and name
} // !"new".equals(tunnelType)
if (!valid) {
%><a href="edit?tunnel=<%=curTunnel%>"><%=intl._t("Go back and edit the tunnel")%></a><%
}
%>
</div>
<%
if (false && valid) {
%>
<div id="globalOperationsPanel" class="panel">
<div class="header"></div>
<div class="footer">
<div class="toolbox">
<input type="hidden" value="true" name="removeConfirm" />
<button id="controlCancel" class="control" type="submit" name="action" value="" title="Cancel"><%=intl._t("Cancel")%></button>
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="authenticate" title="Generate Authentication"><%=intl._t("Generate")%>(<span class="accessKey">S</span>)</button>
</div>
</div>
</div>
<%
} // valid
%>
</form>
<div id="pageFooter">
</div>
<%
} else {
%>Tunnels are not initialized yet, please reload in two minutes.<%
} // isInitialized()
%>
</body>
</html>

View File

@ -32,6 +32,11 @@
<url-pattern>/wizard</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.i2ptunnel.jsp.register_jsp</servlet-name>
<url-pattern>/register</url-pattern>
</servlet-mapping>
<!-- this webapp doesn't actually use sessions or cookies -->
<session-config>
<session-timeout>

View File

@ -139,7 +139,7 @@ public class HostTxtEntry {
out.write(KV_SEPARATOR);
out.write(dest);
}
writeProps(out, false, false);
writeProps(out);
out.newLine();
}
@ -149,21 +149,38 @@ public class HostTxtEntry {
* Includes newline.
* Must have been constructed with non-null properties.
*/
public void writeRemove(BufferedWriter out) throws IOException {
public void writeRemoveLine(BufferedWriter out) throws IOException {
writeRemove(out);
out.newLine();
}
/**
* Write as a "remove" line #!dest=dest#name=name#k1=v1#sig=sig...]
* This works whether constructed with name and dest, or just properties.
* Does not include newline.
* Must have been constructed with non-null properties.
*/
public void writeRemove(Writer out) throws IOException {
if (props == null)
throw new IllegalStateException();
if (name != null && dest != null) {
props.setProperty(PROP_NAME, name);
props.setProperty(PROP_DEST, dest);
}
writeProps(out, false, false);
out.newLine();
writeProps(out);
if (name != null && dest != null) {
props.remove(PROP_NAME);
props.remove(PROP_DEST);
}
}
/**
* Write the props part (if any) only, without newline
*/
public void writeProps(Writer out) throws IOException {
writeProps(out, false, false);
}
/**
* Write the props part (if any) only, without newline
*/
@ -346,20 +363,23 @@ public class HostTxtEntry {
/**
* Sign and set the "sig" property
* Must have been constructed with non-null properties.
*/
private void sign(SigningPrivateKey spk) {
public void sign(SigningPrivateKey spk) {
signIt(spk, PROP_SIG);
}
/**
* Sign and set the "oldsig" property
* Must have been constructed with non-null properties.
*/
private void signInner(SigningPrivateKey spk) {
public void signInner(SigningPrivateKey spk) {
signIt(spk, PROP_OLDSIG);
}
/**
* Sign as a "remove" line #!dest=dest#name=name#k1=v1#sig=sig...]
* Must have been constructed with non-null properties.
*/
public void signRemove(SigningPrivateKey spk) {
if (props == null)
@ -370,7 +390,7 @@ public class HostTxtEntry {
props.setProperty(PROP_DEST, dest);
StringWriter buf = new StringWriter(1024);
try {
writeProps(buf, false, false);
writeProps(buf);
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
@ -395,7 +415,7 @@ public class HostTxtEntry {
buf.append(KV_SEPARATOR);
buf.append(dest);
try {
writeProps(buf, false, false);
writeProps(buf);
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
@ -490,7 +510,7 @@ public class HostTxtEntry {
//out.write("Remove entry:\n");
sw = new StringWriter(1024);
buf = new BufferedWriter(sw);
he.writeRemove(buf);
he.writeRemoveLine(buf);
buf.flush();
out.write(sw.toString());
out.flush();

View File

@ -455,11 +455,29 @@ public class PrivateKeyFile {
return c;
}
/**
* @return null on error or if not initialized
*/
public PrivateKey getPrivKey() {
try {
// call this to force initialization
getDestination();
} catch (Exception e) {
return null;
}
return this.privKey;
}
/**
* @return null on error or if not initialized
*/
public SigningPrivateKey getSigningPrivKey() {
try {
// call this to force initialization
getDestination();
} catch (Exception e) {
return null;
}
return this.signingPrivKey;
}