update
This commit is contained in:
@ -45,6 +45,12 @@ export class CommandHandler implements ICommandHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're in the middle of an AUTH LOGIN sequence
|
||||
if ((session as any).authLoginState) {
|
||||
await this.handleAuthLoginResponse(socket, session, commandLine);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle raw data chunks from connection manager during DATA mode
|
||||
if (commandLine.startsWith('__RAW_DATA__')) {
|
||||
const rawData = commandLine.substring('__RAW_DATA__'.length);
|
||||
@ -780,8 +786,167 @@ export class CommandHandler implements ICommandHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Simple response for now - authentication would be implemented in the security handler
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} Authentication not implemented yet`);
|
||||
// Parse AUTH command
|
||||
const parts = args.trim().split(/\s+/);
|
||||
const method = parts[0]?.toUpperCase();
|
||||
const initialResponse = parts[1];
|
||||
|
||||
// Check if method is supported
|
||||
const supportedMethods = this.smtpServer.getOptions().auth.methods.map(m => m.toUpperCase());
|
||||
if (!method || !supportedMethods.includes(method)) {
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} Unsupported authentication method`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle different authentication methods
|
||||
switch (method) {
|
||||
case 'PLAIN':
|
||||
this.handleAuthPlain(socket, session, initialResponse);
|
||||
break;
|
||||
case 'LOGIN':
|
||||
this.handleAuthLogin(socket, session, initialResponse);
|
||||
break;
|
||||
default:
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} ${method} authentication not implemented`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AUTH PLAIN authentication
|
||||
* @param socket - Client socket
|
||||
* @param session - Session
|
||||
* @param initialResponse - Optional initial response
|
||||
*/
|
||||
private async handleAuthPlain(socket: plugins.net.Socket | plugins.tls.TLSSocket, session: ISmtpSession, initialResponse?: string): Promise<void> {
|
||||
try {
|
||||
let credentials: string;
|
||||
|
||||
if (initialResponse) {
|
||||
// Credentials provided with AUTH PLAIN command
|
||||
credentials = initialResponse;
|
||||
} else {
|
||||
// Request credentials
|
||||
this.sendResponse(socket, '334');
|
||||
|
||||
// Wait for credentials
|
||||
credentials = await this.waitForAuthResponse(socket);
|
||||
}
|
||||
|
||||
// Decode PLAIN credentials (base64 encoded: authzid\0authcid\0password)
|
||||
const decoded = Buffer.from(credentials, 'base64').toString('utf8');
|
||||
const parts = decoded.split('\0');
|
||||
|
||||
if (parts.length !== 3) {
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} Invalid credentials format`);
|
||||
return;
|
||||
}
|
||||
|
||||
const [authzid, authcid, password] = parts;
|
||||
const username = authcid || authzid; // Use authcid if provided, otherwise authzid
|
||||
|
||||
// Authenticate using security handler
|
||||
const authenticated = await this.smtpServer.getSecurityHandler().authenticate({
|
||||
username,
|
||||
password,
|
||||
mechanism: 'PLAIN'
|
||||
});
|
||||
|
||||
if (authenticated) {
|
||||
session.authenticated = true;
|
||||
session.username = username;
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTHENTICATION_SUCCESSFUL} Authentication successful`);
|
||||
} else {
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} Authentication failed`);
|
||||
}
|
||||
} catch (error) {
|
||||
SmtpLogger.error(`AUTH PLAIN error: ${error instanceof Error ? error.message : String(error)}`);
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} Authentication error`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AUTH LOGIN authentication
|
||||
* @param socket - Client socket
|
||||
* @param session - Session
|
||||
* @param initialResponse - Optional initial response
|
||||
*/
|
||||
private async handleAuthLogin(socket: plugins.net.Socket | plugins.tls.TLSSocket, session: ISmtpSession, initialResponse?: string): Promise<void> {
|
||||
try {
|
||||
if (initialResponse) {
|
||||
// Username provided with AUTH LOGIN command
|
||||
const username = Buffer.from(initialResponse, 'base64').toString('utf8');
|
||||
(session as any).authLoginState = 'waiting_password';
|
||||
(session as any).authLoginUsername = username;
|
||||
// Request password
|
||||
this.sendResponse(socket, '334 UGFzc3dvcmQ6'); // Base64 for "Password:"
|
||||
} else {
|
||||
// Request username
|
||||
(session as any).authLoginState = 'waiting_username';
|
||||
this.sendResponse(socket, '334 VXNlcm5hbWU6'); // Base64 for "Username:"
|
||||
}
|
||||
} catch (error) {
|
||||
SmtpLogger.error(`AUTH LOGIN error: ${error instanceof Error ? error.message : String(error)}`);
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} Authentication error`);
|
||||
delete (session as any).authLoginState;
|
||||
delete (session as any).authLoginUsername;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AUTH LOGIN response
|
||||
* @param socket - Client socket
|
||||
* @param session - Session
|
||||
* @param response - Response from client
|
||||
*/
|
||||
private async handleAuthLoginResponse(socket: plugins.net.Socket | plugins.tls.TLSSocket, session: ISmtpSession, response: string): Promise<void> {
|
||||
const trimmedResponse = response.trim();
|
||||
|
||||
// Check for cancellation
|
||||
if (trimmedResponse === '*') {
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} Authentication cancelled`);
|
||||
delete (session as any).authLoginState;
|
||||
delete (session as any).authLoginUsername;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if ((session as any).authLoginState === 'waiting_username') {
|
||||
// We received the username
|
||||
const username = Buffer.from(trimmedResponse, 'base64').toString('utf8');
|
||||
(session as any).authLoginUsername = username;
|
||||
(session as any).authLoginState = 'waiting_password';
|
||||
// Request password
|
||||
this.sendResponse(socket, '334 UGFzc3dvcmQ6'); // Base64 for "Password:"
|
||||
} else if ((session as any).authLoginState === 'waiting_password') {
|
||||
// We received the password
|
||||
const password = Buffer.from(trimmedResponse, 'base64').toString('utf8');
|
||||
const username = (session as any).authLoginUsername;
|
||||
|
||||
// Clear auth state
|
||||
delete (session as any).authLoginState;
|
||||
delete (session as any).authLoginUsername;
|
||||
|
||||
// Authenticate using security handler
|
||||
const authenticated = await this.smtpServer.getSecurityHandler().authenticate({
|
||||
username,
|
||||
password,
|
||||
mechanism: 'LOGIN'
|
||||
});
|
||||
|
||||
if (authenticated) {
|
||||
session.authenticated = true;
|
||||
session.username = username;
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTHENTICATION_SUCCESSFUL} Authentication successful`);
|
||||
} else {
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} Authentication failed`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
SmtpLogger.error(`AUTH LOGIN response error: ${error instanceof Error ? error.message : String(error)}`);
|
||||
this.sendResponse(socket, `${SmtpResponseCode.AUTH_FAILED} Authentication error`);
|
||||
delete (session as any).authLoginState;
|
||||
delete (session as any).authLoginUsername;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user