Identd: fix various issues

There's a bunch of sub optimal behavior from our ident server.
For one, it allows user enumeration which we don't really want and it doesn't clean up connections that don't send any data.

Fix that
This commit is contained in:
Reto Brunner 2024-05-12 11:51:18 +02:00
commit 0955d9df06

View file

@ -1,9 +1,9 @@
import log from "./log";
import fs from "fs"; import fs from "fs";
import net, {Socket} from "net"; import net, {Socket} from "net";
import colors from "chalk"; import colors from "chalk";
import Helper from "./helper"; import Helper from "./helper";
import Config from "./config"; import Config from "./config";
import log from "./log";
type Connection = { type Connection = {
socket: Socket; socket: Socket;
@ -66,31 +66,56 @@ class Identification {
serverConnection(socket: Socket) { serverConnection(socket: Socket) {
socket.on("error", (err: string) => log.error(`Identd socket error: ${err}`)); socket.on("error", (err: string) => log.error(`Identd socket error: ${err}`));
socket.on("data", (data) => { socket.setTimeout(5000, () => {
log.warn(
`identd: no data received, closing connection to ${
socket.remoteAddress || "undefined"
}`
);
socket.destroy();
});
socket.once("data", (data) => {
this.respondToIdent(socket, data); this.respondToIdent(socket, data);
socket.end(); socket.end();
}); });
} }
respondToIdent(socket: Socket, buffer: Buffer) { respondToIdent(socket: Socket, buffer: Buffer) {
if (!socket.remoteAddress) {
log.warn("identd: no remote address");
return;
}
const data = buffer.toString().split(","); const data = buffer.toString().split(",");
const lport = parseInt(data[0], 10) || 0; const lport = parseInt(data[0], 10) || 0;
const fport = parseInt(data[1], 10) || 0; const fport = parseInt(data[1], 10) || 0;
if (lport < 1 || fport < 1 || lport > 65535 || fport > 65535) { if (lport < 1 || fport < 1 || lport > 65535 || fport > 65535) {
log.warn(`identd: bogus request from ${socket.remoteAddress}`);
return; return;
} }
log.debug(`identd: remote ${socket.remoteAddress} query ${lport}, ${fport}`);
for (const connection of this.connections.values()) { for (const connection of this.connections.values()) {
if (connection.socket.remotePort === fport && connection.socket.localPort === lport) { // we only want to respond if all the ip,port tuples match, to avoid user enumeration
return socket.write( if (
`${lport}, ${fport} : USERID : TheLounge : ${connection.user}\r\n` connection.socket.remotePort === fport &&
); connection.socket.localPort === lport &&
socket.remoteAddress === connection.socket.remoteAddress &&
socket.localAddress === connection.socket.localAddress
) {
const reply = `${lport}, ${fport} : USERID : TheLounge : ${connection.user}\r\n`;
log.debug(`identd: reply is ${reply.trimEnd()}`);
socket.write(reply);
return;
} }
} }
socket.write(`${lport}, ${fport} : ERROR : NO-USER\r\n`); const reply = `${lport}, ${fport} : ERROR : NO-USER\r\n`;
log.debug(`identd: reply is ${reply.trimEnd()}`);
socket.write(reply);
} }
addSocket(socket: Socket, user: string) { addSocket(socket: Socket, user: string) {
@ -127,8 +152,21 @@ class Identification {
return; return;
} }
if (!connection.socket.remoteAddress) {
log.warn(`oidentd: socket has no remote address, will not respond to queries`);
return;
}
if (!connection.socket.localAddress) {
log.warn(`oidentd: socket has no local address, will not respond to queries`);
return;
}
// we only want to respond if all the ip,port tuples match, to avoid user enumeration
file += file +=
`fport ${connection.socket.remotePort}` + `to ${connection.socket.remoteAddress}` +
` fport ${connection.socket.remotePort}` +
` from ${connection.socket.localAddress}` +
` lport ${connection.socket.localPort}` + ` lport ${connection.socket.localPort}` +
` { reply "${connection.user}" }\n`; ` { reply "${connection.user}" }\n`;
}); });