1 17 package org.alfresco.repo.security.authentication.ntlm; 18 19 import java.io.IOException ; 20 import java.net.InetAddress ; 21 import java.net.UnknownHostException ; 22 import java.security.NoSuchAlgorithmException ; 23 import java.security.Provider ; 24 import java.security.Security ; 25 import java.util.Enumeration ; 26 import java.util.Hashtable ; 27 28 import org.alfresco.error.AlfrescoRuntimeException; 29 import org.alfresco.filesys.server.auth.PasswordEncryptor; 30 import org.alfresco.filesys.server.auth.passthru.AuthenticateSession; 31 import org.alfresco.filesys.server.auth.passthru.PassthruServers; 32 import org.alfresco.filesys.smb.SMBException; 33 import org.alfresco.filesys.smb.SMBStatus; 34 import org.apache.commons.logging.Log; 35 import org.apache.commons.logging.LogFactory; 36 37 import net.sf.acegisecurity.*; 38 import net.sf.acegisecurity.providers.*; 39 40 45 public class NTLMAuthenticationProvider implements AuthenticationProvider 46 { 47 private static final Log logger = LogFactory.getLog("org.alfresco.acegi"); 48 49 53 public static final String NTLMAuthorityGuest = "Guest"; 54 public static final String NTLMAuthorityAdministrator = "Administrator"; 55 56 58 private static final long DefaultSessionTimeout = 60000L; private static final long MinimumSessionTimeout = 5000L; 61 63 private PassthruServers m_passthruServers; 64 65 67 private PasswordEncryptor m_encryptor; 68 69 71 private boolean m_allowGuest; 72 73 78 private Hashtable <NTLMPassthruToken,AuthenticateSession> m_passthruSessions; 79 80 82 private long m_passthruSessTmo = DefaultSessionTimeout; 83 84 86 private PassthruReaperThread m_reaperThread; 87 88 91 class PassthruReaperThread extends Thread 92 { 93 95 private boolean m_ishutdown; 96 97 99 private long m_wakeupInterval = m_passthruSessTmo / 2; 100 101 104 PassthruReaperThread() 105 { 106 setDaemon(true); 107 setName("PassthruReaper"); 108 start(); 109 } 110 111 116 public final void setWakeup(long wakeup) 117 { 118 m_wakeupInterval = wakeup; 119 } 120 121 124 public void run() 125 { 126 128 m_ishutdown = false; 129 130 while ( m_ishutdown == false) 131 { 132 134 try 135 { 136 sleep( m_wakeupInterval); 137 } 138 catch ( InterruptedException ex) 139 { 140 } 141 142 144 if ( m_passthruSessions.size() > 0) 145 { 146 148 Enumeration <NTLMPassthruToken> tokenEnum = m_passthruSessions.keys(); 149 long timeNow = System.currentTimeMillis(); 150 151 while (tokenEnum.hasMoreElements()) 152 { 153 155 NTLMPassthruToken ntlmToken = tokenEnum.nextElement(); 156 157 if ( ntlmToken != null && ntlmToken.getAuthenticationExpireTime() < timeNow) 158 { 159 161 AuthenticateSession authSess = m_passthruSessions.get(ntlmToken); 162 if ( authSess != null) 163 { 164 try 165 { 166 168 authSess.CloseSession(); 169 } 170 catch ( Exception ex) 171 { 172 174 if(logger.isDebugEnabled()) 175 logger.debug("Error closing expired authentication session", ex); 176 } 177 } 178 179 181 m_passthruSessions.remove(ntlmToken); 182 183 185 if(logger.isDebugEnabled()) 186 logger.debug("Removed expired NTLM token " + ntlmToken); 187 } 188 } 189 } 190 } 191 192 194 if(logger.isDebugEnabled()) 195 logger.debug("Passthru reaper thread shutdown"); 196 } 197 198 201 public final void shutdownRequest() 202 { 203 m_ishutdown = true; 204 this.interrupt(); 205 } 206 } 207 208 211 public NTLMAuthenticationProvider() { 212 213 215 m_passthruServers = new PassthruServers(); 216 217 219 m_encryptor = new PasswordEncryptor(); 220 221 223 m_passthruSessions = new Hashtable <NTLMPassthruToken,AuthenticateSession>(); 224 m_reaperThread = new PassthruReaperThread(); 225 } 226 227 234 public Authentication authenticate(Authentication auth) throws AuthenticationException 235 { 236 238 if ( logger.isDebugEnabled()) 239 logger.debug("Authenticate " + auth); 240 241 243 if( auth instanceof NTLMPassthruToken) 244 { 245 247 NTLMPassthruToken ntlmToken = (NTLMPassthruToken) auth; 248 249 251 authenticatePassthru(ntlmToken); 252 } 253 254 256 else if( auth instanceof NTLMLocalToken) 257 { 258 AuthenticateSession authSess = null; 259 260 try 261 { 262 263 265 NTLMLocalToken ntlmToken = (NTLMLocalToken) auth; 266 267 269 authSess = m_passthruServers.openSession(); 270 271 273 authenticateLocal(ntlmToken, authSess); 274 } 275 finally 276 { 277 279 if ( authSess != null) 280 { 281 try 282 { 283 authSess.CloseSession(); 284 } 285 catch ( Exception ex) 286 { 287 } 288 } 289 } 290 } 291 292 294 return auth; 295 } 296 297 302 public boolean supports(Class authentication) 303 { 304 306 if ( NTLMPassthruToken.class.isAssignableFrom(authentication)) 307 return true; 308 return NTLMLocalToken.class.isAssignableFrom(authentication); 309 } 310 311 316 public final boolean allowsGuest() 317 { 318 return m_allowGuest; 319 } 320 321 326 public final void setDomain(String domain) { 327 328 330 if ( m_passthruServers.getTotalServerCount() > 0) 331 throw new AlfrescoRuntimeException("Passthru server list already configured"); 332 333 335 m_passthruServers.setDomain(domain); 336 } 337 338 343 public final void setServers(String servers) { 344 345 347 if ( m_passthruServers.getTotalServerCount() > 0) 348 throw new AlfrescoRuntimeException("Passthru server list already configured"); 349 350 352 m_passthruServers.setServerList(servers); 353 } 354 355 360 public final void setUseLocalServer(String useLocal) 361 { 362 364 if ( Boolean.parseBoolean(useLocal) == true) 365 { 366 368 if ( m_passthruServers.getTotalServerCount() > 0) 369 throw new AlfrescoRuntimeException("Passthru server list already configured"); 370 371 try 372 { 373 375 InetAddress [] localAddrs = InetAddress.getAllByName(InetAddress.getLocalHost().getHostName()); 376 377 379 if ( localAddrs != null && localAddrs.length > 0) 380 { 381 StringBuilder addrStr = new StringBuilder (); 382 383 for ( InetAddress curAddr : localAddrs) 384 { 385 if ( curAddr.isLoopbackAddress() == false) 386 { 387 addrStr.append(curAddr.getHostAddress()); 388 addrStr.append(","); 389 } 390 } 391 392 if ( addrStr.length() > 0) 393 addrStr.setLength(addrStr.length() - 1); 394 395 397 m_passthruServers.setServerList(addrStr.toString()); 398 } 399 else 400 throw new AlfrescoRuntimeException("No local server address(es)"); 401 } 402 catch ( UnknownHostException ex) 403 { 404 throw new AlfrescoRuntimeException("Failed to get local address list"); 405 } 406 } 407 } 408 409 414 public final void setGuestAccess(String guest) 415 { 416 m_allowGuest = Boolean.parseBoolean(guest); 417 } 418 419 424 public final void setJCEProvider(String providerClass) 425 { 426 429 try 430 { 431 432 434 Object jceObj = Class.forName(providerClass).newInstance(); 435 if (jceObj instanceof java.security.Provider ) 436 { 437 438 440 Provider jceProvider = (Provider ) jceObj; 441 442 444 Security.addProvider(jceProvider); 445 446 448 if ( logger.isDebugEnabled()) 449 logger.debug("Using JCE provider " + providerClass); 450 } 451 else 452 { 453 throw new AlfrescoRuntimeException("JCE provider class is not a valid Provider class"); 454 } 455 } 456 catch (ClassNotFoundException ex) 457 { 458 throw new AlfrescoRuntimeException("JCE provider class " + providerClass + " not found"); 459 } 460 catch (Exception ex) 461 { 462 throw new AlfrescoRuntimeException("JCE provider class error", ex); 463 } 464 } 465 466 471 public final void setSessionTimeout(String sessTmo) 472 { 473 475 try 476 { 477 479 long sessTmoMilli = Long.parseLong(sessTmo) * 1000L; 480 481 if ( sessTmoMilli < MinimumSessionTimeout) 482 throw new AlfrescoRuntimeException("Authentication session timeout too low, " + sessTmo); 483 484 486 m_passthruSessTmo = sessTmoMilli; 487 488 490 m_reaperThread.setWakeup( sessTmoMilli / 2); 491 } 492 catch(NumberFormatException ex) 493 { 494 throw new AlfrescoRuntimeException("Invalid authenication session timeout value"); 495 } 496 } 497 498 503 private final long getSessionTimeout() 504 { 505 return m_passthruSessTmo; 506 } 507 508 514 private void authenticateLocal(NTLMLocalToken ntlmToken, AuthenticateSession authSess) 515 { 516 try 517 { 518 520 String username = (String ) ntlmToken.getPrincipal(); 521 String plainPwd = (String ) ntlmToken.getCredentials(); 522 byte[] ntlm1Pwd = m_encryptor.generateEncryptedPassword( plainPwd, authSess.getEncryptionKey(), PasswordEncryptor.NTLM1); 523 524 528 authSess.doSessionSetup(username, null, ntlm1Pwd); 529 530 532 if ( authSess.isGuest() || username.equalsIgnoreCase("GUEST")) 533 { 534 536 if ( allowsGuest()) 537 { 538 540 GrantedAuthority[] authorities = new GrantedAuthority[1]; 541 authorities[0] = new GrantedAuthorityImpl(NTLMAuthorityGuest); 542 543 ntlmToken.setAuthorities(authorities); 544 } 545 else 546 { 547 549 throw new BadCredentialsException("Guest logons disabled"); 550 } 551 } 552 553 555 ntlmToken.setAuthenticated(true); 556 } 557 catch (NoSuchAlgorithmException ex) 558 { 559 561 throw new AuthenticationServiceException("JCE provider error", ex); 562 } 563 catch (IOException ex) 564 { 565 567 throw new AuthenticationServiceException("I/O error", ex); 568 } 569 catch (SMBException ex) 570 { 571 573 if ( ex.getErrorClass() == SMBStatus.NTErr) 574 { 575 AuthenticationException authEx = null; 576 577 switch( ex.getErrorCode()) 578 { 579 case SMBStatus.NTLogonFailure: 580 authEx = new BadCredentialsException("Logon failure"); 581 break; 582 case SMBStatus.NTAccountDisabled: 583 authEx = new DisabledException("Account disabled"); 584 break; 585 default: 586 authEx = new BadCredentialsException("Logon failure"); 587 break; 588 } 589 590 throw authEx; 591 } 592 else 593 throw new BadCredentialsException("Logon failure"); 594 } 595 } 596 597 602 private void authenticatePassthru(NTLMPassthruToken ntlmToken) 603 { 604 607 AuthenticateSession authSess = m_passthruSessions.get(ntlmToken); 608 609 if ( authSess == null) 610 { 611 614 if ( ntlmToken.getChallenge() != null) 615 throw new CredentialsExpiredException("Authentication session expired"); 616 617 619 authSess = m_passthruServers.openSession(); 620 621 ntlmToken.setAuthenticationExpireTime(System.currentTimeMillis() + getSessionTimeout()); 622 623 625 ntlmToken.setChallenge(new NTLMChallenge(authSess.getEncryptionKey())); 626 627 StringBuilder details = new StringBuilder (); 628 629 631 details.append(authSess.getDomain()); 632 details.append("\\"); 633 details.append(authSess.getPCShare().getNodeName()); 634 details.append(","); 635 details.append(authSess.getSession().getProtocolName()); 636 637 ntlmToken.setDetails(details.toString()); 638 639 641 m_passthruSessions.put(ntlmToken, authSess); 642 643 645 if ( logger.isDebugEnabled()) 646 logger.debug("Passthru stage 1 token " + ntlmToken); 647 } 648 else 649 { 650 try 651 { 652 654 byte[] lmPwd = null; 655 byte[] ntlmPwd = null; 656 657 if ( ntlmToken.getPasswordType() == PasswordEncryptor.LANMAN) 658 lmPwd = ntlmToken.getHashedPassword(); 659 else if ( ntlmToken.getPasswordType() == PasswordEncryptor.NTLM1) 660 ntlmPwd = ntlmToken.getHashedPassword(); 661 662 String username = (String ) ntlmToken.getPrincipal(); 663 664 authSess.doSessionSetup(username, lmPwd, ntlmPwd); 665 666 668 if ( authSess.isGuest() || username.equalsIgnoreCase("GUEST")) 669 { 670 672 if ( allowsGuest()) 673 { 674 676 GrantedAuthority[] authorities = new GrantedAuthority[1]; 677 authorities[0] = new GrantedAuthorityImpl(NTLMAuthorityGuest); 678 679 ntlmToken.setAuthorities(authorities); 680 } 681 else 682 { 683 685 throw new BadCredentialsException("Guest logons disabled"); 686 } 687 } 688 689 691 ntlmToken.setAuthenticated(true); 692 } 693 catch (IOException ex) 694 { 695 697 throw new AuthenticationServiceException("I/O error", ex); 698 } 699 catch (SMBException ex) 700 { 701 703 if ( ex.getErrorClass() == SMBStatus.NTErr) 704 { 705 AuthenticationException authEx = null; 706 707 switch( ex.getErrorCode()) 708 { 709 case SMBStatus.NTLogonFailure: 710 authEx = new BadCredentialsException("Logon failure"); 711 break; 712 case SMBStatus.NTAccountDisabled: 713 authEx = new DisabledException("Account disabled"); 714 break; 715 default: 716 authEx = new BadCredentialsException("Logon failure"); 717 break; 718 } 719 720 throw authEx; 721 } 722 else 723 throw new BadCredentialsException("Logon failure"); 724 } 725 finally 726 { 727 729 if ( authSess != null) 730 { 731 try 732 { 733 735 m_passthruSessions.remove(ntlmToken); 736 737 739 authSess.CloseSession(); 740 } 741 catch (Exception ex) 742 { 743 } 744 } 745 } 746 } 747 } 748 } 749 | Popular Tags |