Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412/** * SMB2/3 Authentication Manager * Handles user authentication, session security, and access control */
import type { SMB2ServerConfig, SMB2Session, SMB2Connection, SMB2UserConfig,} from '../protocol/types.js';import { Logger } from '../utils/logger.js';import { generateRandomBytes, calculateSha256, constantTimeCompare } from '../utils/helpers.js';
export interface AuthenticationResult { readonly success: boolean; readonly userId?: string; readonly isGuest: boolean; readonly isAnonymous: boolean; readonly sessionKey?: Buffer; readonly error?: string;}
export interface SecurityContext { readonly userId: string; readonly isAuthenticated: boolean; readonly isGuest: boolean; readonly isAnonymous: boolean; readonly groups: string[]; readonly permissions: string[]; readonly sessionKey: Buffer; readonly signKey: Buffer; readonly applicationKey: Buffer;}
export class SMB2AuthenticationManager { private readonly config: SMB2ServerConfig; private readonly logger: Logger; private readonly users: Map<string, SMB2UserConfig>; private readonly activeSessions: Map<bigint, SecurityContext>;
constructor(config: SMB2ServerConfig) { this.config = config; this.logger = new Logger('SMB2AuthManager'); this.users = new Map(); this.activeSessions = new Map();
// Load users from configuration this.loadUsers(); }
/** * Authenticate a user with username and password */ public async authenticateUser( username: string, password: string, connection: SMB2Connection ): Promise<AuthenticationResult> { try { this.logger.debug(`Authentication attempt for user: ${username}`);
// Handle anonymous authentication if (!username || username.toLowerCase() === 'anonymous') { return this.handleAnonymousAuth(); }
// Handle guest authentication if (username.toLowerCase() === 'guest') { return this.handleGuestAuth(); }
// Handle regular user authentication const user = this.users.get(username.toLowerCase()); if (!user) { this.logger.warn(`Authentication failed: user not found - ${username}`); return { success: false, isGuest: false, isAnonymous: false, error: 'User not found', }; }
// Verify password const isValidPassword = await this.verifyPassword(password, user.passwordHash); if (!isValidPassword) { this.logger.warn(`Authentication failed: invalid password for user - ${username}`); return { success: false, isGuest: false, isAnonymous: false, error: 'Invalid password', }; }
// Generate session key const sessionKey = this.generateSessionKey(username, password);
this.logger.info(`Authentication successful for user: ${username}`); return { success: true, userId: username, isGuest: user.isGuest, isAnonymous: false, sessionKey, };
} catch (error) { this.logger.error('Authentication error:', error); return { success: false, isGuest: false, isAnonymous: false, error: 'Authentication failed', }; } }
/** * Authenticate using NTLM challenge/response */ public async authenticateNTLM( ntlmToken: Buffer, connection: SMB2Connection ): Promise<AuthenticationResult> { try { this.logger.debug('NTLM authentication attempt');
// Parse NTLM token (stub implementation) const ntlmData = this.parseNTLMToken(ntlmToken); if (!ntlmData) { return { success: false, isGuest: false, isAnonymous: false, error: 'Invalid NTLM token', }; }
// For now, just accept any NTLM token and create a guest session // In a real implementation, this would verify the NTLM response this.logger.warn('NTLM authentication is stubbed - allowing guest access'); return this.handleGuestAuth();
} catch (error) { this.logger.error('NTLM authentication error:', error); return { success: false, isGuest: false, isAnonymous: false, error: 'NTLM authentication failed', }; } }
/** * Create security context for authenticated session */ public createSecurityContext( sessionId: bigint, authResult: AuthenticationResult ): SecurityContext { const context: SecurityContext = { userId: authResult.userId || 'anonymous', isAuthenticated: authResult.success, isGuest: authResult.isGuest, isAnonymous: authResult.isAnonymous, groups: this.getUserGroups(authResult.userId), permissions: this.getUserPermissions(authResult.userId), sessionKey: authResult.sessionKey || Buffer.alloc(32), signKey: this.deriveSigningKey(authResult.sessionKey || Buffer.alloc(32)), applicationKey: this.deriveApplicationKey(authResult.sessionKey || Buffer.alloc(32)), };
this.activeSessions.set(sessionId, context); this.logger.debug(`Security context created for session ${sessionId}`);
return context; }
/** * Get security context for session */ public getSecurityContext(sessionId: bigint): SecurityContext | undefined { return this.activeSessions.get(sessionId); }
/** * Remove security context for session */ public removeSecurityContext(sessionId: bigint): void { this.activeSessions.delete(sessionId); this.logger.debug(`Security context removed for session ${sessionId}`); }
/** * Check if user has access to share */ public checkShareAccess(userId: string, shareName: string): boolean { if (!userId || userId === 'anonymous') { // Check if anonymous access is allowed for this share return true; // For now, allow anonymous access }
const user = this.users.get(userId.toLowerCase()); if (!user) { return false; }
// Check if user has access to this share return user.allowedShares.length === 0 || user.allowedShares.includes(shareName); }
/** * Generate authentication challenge */ public generateChallenge(): Buffer { return generateRandomBytes(8); }
/** * Load users from configuration */ private loadUsers(): void { if (!this.config.users) { this.logger.info('No users configured - anonymous access only'); return; }
for (const user of this.config.users) { this.users.set(user.username.toLowerCase(), user); this.logger.debug(`Loaded user: ${user.username} (guest: ${user.isGuest})`); }
this.logger.info(`Loaded ${this.users.size} users`); }
/** * Handle anonymous authentication */ private handleAnonymousAuth(): AuthenticationResult { this.logger.debug('Anonymous authentication'); return { success: true, userId: 'anonymous', isGuest: false, isAnonymous: true, sessionKey: Buffer.alloc(32), // Empty session key for anonymous }; }
/** * Handle guest authentication */ private handleGuestAuth(): AuthenticationResult { this.logger.debug('Guest authentication'); const sessionKey = this.generateSessionKey('guest', ''); return { success: true, userId: 'guest', isGuest: true, isAnonymous: false, sessionKey, }; }
/** * Verify password against hash */ private async verifyPassword(password: string, passwordHash: string): Promise<boolean> { // Simple SHA-256 hash verification (in production, use proper password hashing like bcrypt) const hash = calculateSha256(password); return constantTimeCompare(Buffer.from(hash, 'hex'), Buffer.from(passwordHash, 'hex')); }
/** * Generate session key from username and password */ private generateSessionKey(username: string, password: string): Buffer { const combined = `${username}:${password}:${Date.now()}`; const hash = calculateSha256(combined); return Buffer.from(hash, 'hex'); }
/** * Derive signing key from session key */ private deriveSigningKey(sessionKey: Buffer): Buffer { const signKeyMaterial = Buffer.concat([sessionKey, Buffer.from('SMB2AESCMAC', 'ascii')]); return Buffer.from(calculateSha256(signKeyMaterial), 'hex').subarray(0, 16); }
/** * Derive application key from session key */ private deriveApplicationKey(sessionKey: Buffer): Buffer { const appKeyMaterial = Buffer.concat([sessionKey, Buffer.from('SMB2APP', 'ascii')]); return Buffer.from(calculateSha256(appKeyMaterial), 'hex').subarray(0, 16); }
/** * Get user groups */ private getUserGroups(userId?: string): string[] { if (!userId || userId === 'anonymous') { return ['anonymous']; }
const user = this.users.get(userId.toLowerCase()); if (!user) { return []; }
const groups = ['users']; if (user.isGuest) { groups.push('guests'); } if (user.isAdmin) { groups.push('administrators'); }
return groups; }
/** * Get user permissions */ private getUserPermissions(userId?: string): string[] { if (!userId || userId === 'anonymous') { return ['read']; }
const user = this.users.get(userId.toLowerCase()); if (!user) { return []; }
const permissions = ['read']; if (!user.isGuest) { permissions.push('write'); } if (user.isAdmin) { permissions.push('admin'); }
return permissions; }
/** * Parse NTLM token (stub implementation) */ private parseNTLMToken(token: Buffer): any | null { try { // This is a stub - a real implementation would parse the NTLM token structure if (token.length < 8) { return null; }
// Return dummy parsed data return { messageType: token.readUInt32LE(0), flags: token.readUInt32LE(4), }; } catch { return null; } }
/** * Add user at runtime */ public addUser(user: SMB2UserConfig): void { this.users.set(user.username.toLowerCase(), user); this.logger.info(`User added: ${user.username}`); }
/** * Remove user at runtime */ public removeUser(username: string): boolean { const removed = this.users.delete(username.toLowerCase()); if (removed) { this.logger.info(`User removed: ${username}`); } return removed; }
/** * Get user list */ public getUsers(): string[] { return Array.from(this.users.keys()); }
/** * Get active session count */ public getActiveSessionCount(): number { return this.activeSessions.size; }
/** * Cleanup expired sessions */ public cleanupExpiredSessions(): void { // This would be implemented to remove expired sessions // For now, it's a placeholder this.logger.debug('Session cleanup completed'); }}