1 7 8 9 package com.sun.security.auth.module; 10 11 import java.io.*; 12 import java.net.*; 13 import java.text.MessageFormat ; 14 import java.util.*; 15 16 import javax.security.auth.*; 17 import javax.security.auth.kerberos.*; 18 import javax.security.auth.callback.*; 19 import javax.security.auth.login.*; 20 import javax.security.auth.spi.*; 21 22 import sun.security.krb5.*; 23 import sun.security.krb5.Config; 24 import sun.security.krb5.RealmException; 25 import sun.security.util.AuthResources; 26 import sun.security.jgss.krb5.Krb5Util; 27 import sun.security.krb5.Credentials; 28 import sun.misc.HexDumpEncoder; 29 30 332 333 public class Krb5LoginModule implements LoginModule { 334 335 private Subject subject; 337 private CallbackHandler callbackHandler; 338 private Map sharedState; 339 private Map options; 340 341 private boolean debug = false; 343 private boolean storeKey = false; 344 private boolean doNotPrompt = false; 345 private boolean useTicketCache = false; 346 private boolean useKeyTab = false; 347 private String ticketCacheName = null; 348 private String keyTabName = null; 349 private String princName = null; 350 351 private boolean useFirstPass = false; 352 private boolean tryFirstPass = false; 353 private boolean storePass = false; 354 private boolean clearPass = false; 355 private boolean refreshKrb5Config = false; 356 private boolean renewTGT = false; 357 358 private boolean isInitiator = true; 361 362 private boolean succeeded = false; 364 private boolean commitSucceeded = false; 365 private String username; 366 private EncryptionKey[] encKeys = null; 367 private Credentials cred = null; 368 369 private PrincipalName principal = null; 370 private KerberosPrincipal kerbClientPrinc = null; 371 private KerberosTicket kerbTicket = null; 372 private KerberosKey[] kerbKeys = null; 373 private StringBuffer krb5PrincName = null; 374 private char[] password = null; 375 376 private static final String NAME = "javax.security.auth.login.name"; 377 private static final String PWD = "javax.security.auth.login.password"; 378 static final java.util.ResourceBundle rb = 379 java.util.ResourceBundle.getBundle("sun.security.util.AuthResources"); 380 381 397 398 public void initialize(Subject subject, 399 CallbackHandler callbackHandler, 400 Map<String , ?> sharedState, 401 Map<String , ?> options) { 402 403 this.subject = subject; 404 this.callbackHandler = callbackHandler; 405 this.sharedState = sharedState; 406 this.options = options; 407 408 410 debug = "true".equalsIgnoreCase((String )options.get("debug")); 411 storeKey = "true".equalsIgnoreCase((String )options.get("storeKey")); 412 doNotPrompt = "true".equalsIgnoreCase((String )options.get 413 ("doNotPrompt")); 414 useTicketCache = "true".equalsIgnoreCase((String )options.get 415 ("useTicketCache")); 416 useKeyTab = "true".equalsIgnoreCase((String )options.get("useKeyTab")); 417 ticketCacheName = (String )options.get("ticketCache"); 418 keyTabName = (String )options.get("keyTab"); 419 princName = (String )options.get("principal"); 420 refreshKrb5Config = 421 "true".equalsIgnoreCase((String )options.get("refreshKrb5Config")); 422 renewTGT = 423 "true".equalsIgnoreCase((String )options.get("renewTGT")); 424 425 String isInitiatorValue = ((String )options.get("isInitiator")); 427 if (isInitiatorValue == null) { 428 } else { 430 isInitiator = "true".equalsIgnoreCase(isInitiatorValue); 431 } 432 433 tryFirstPass = 434 "true".equalsIgnoreCase 435 ((String )options.get("tryFirstPass")); 436 useFirstPass = 437 "true".equalsIgnoreCase 438 ((String )options.get("useFirstPass")); 439 storePass = 440 "true".equalsIgnoreCase((String )options.get("storePass")); 441 clearPass = 442 "true".equalsIgnoreCase((String )options.get("clearPass")); 443 if (debug) { 444 System.out.print("Debug is " + debug 445 + " storeKey " + storeKey 446 + " useTicketCache " + useTicketCache 447 + " useKeyTab " + useKeyTab 448 + " doNotPrompt " + doNotPrompt 449 + " ticketCache is " + ticketCacheName 450 + " isInitiator " + isInitiator 451 + " KeyTab is " + keyTabName 452 + " refreshKrb5Config is " + refreshKrb5Config 453 + " principal is " + princName 454 + " tryFirstPass is " + tryFirstPass 455 + " useFirstPass is " + useFirstPass 456 + " storePass is " + storePass 457 + " clearPass is " + clearPass + "\n"); 458 } 459 } 460 461 462 475 public boolean login() throws LoginException { 476 477 int len; 478 validateConfiguration(); 479 if (refreshKrb5Config) { 480 try { 481 if (debug) { 482 System.out.println("Refreshing Kerberos configuration"); 483 } 484 sun.security.krb5.Config.refresh(); 485 } catch (KrbException ke) { 486 LoginException le = new LoginException(ke.getMessage()); 487 le.initCause(ke); 488 throw le; 489 } 490 } 491 String principalProperty = System.getProperty 492 ("sun.security.krb5.principal"); 493 if (principalProperty != null) { 494 krb5PrincName = new StringBuffer (principalProperty); 495 } else { 496 if (princName != null) { 497 krb5PrincName = new StringBuffer (princName); 498 } 499 } 500 501 if (tryFirstPass) { 502 try { 503 attemptAuthentication(true); 504 if (debug) 505 System.out.println("\t\t[Krb5LoginModule] " + 506 "authentication succeeded"); 507 succeeded = true; 508 cleanState(); 509 return true; 510 } catch (LoginException le) { 511 cleanState(); 513 if (debug) { 514 System.out.println("\t\t[Krb5LoginModule] " + 515 "tryFirstPass failed with:" + 516 le.getMessage()); 517 } 518 } 519 } else if (useFirstPass) { 520 try { 521 attemptAuthentication(true); 522 succeeded = true; 523 cleanState(); 524 return true; 525 } catch (LoginException e) { 526 if (debug) { 528 System.out.println("\t\t[Krb5LoginModule] " + 529 "authentication failed \n" + 530 e.getMessage()); 531 } 532 succeeded = false; 533 cleanState(); 534 throw e; 535 } 536 } 537 538 541 try { 542 attemptAuthentication(false); 543 succeeded = true; 544 cleanState(); 545 return true; 546 } catch (LoginException e) { 547 if (debug) { 549 System.out.println("\t\t[Krb5LoginModule] " + 550 "authentication failed \n" + 551 e.getMessage()); 552 } 553 succeeded = false; 554 cleanState(); 555 throw e; 556 } 557 } 558 564 565 private void attemptAuthentication(boolean getPasswdFromSharedState) 566 throws LoginException { 567 568 572 if (krb5PrincName != null) { 573 try { 574 principal = new PrincipalName 575 (krb5PrincName.toString(), 576 PrincipalName.KRB_NT_PRINCIPAL); 577 } catch (KrbException e) { 578 LoginException le = new LoginException(e.getMessage()); 579 le.initCause(e); 580 throw le; 581 } 582 } 583 584 try { 585 if (useTicketCache) { 586 if (debug) 588 System.out.println("Acquire TGT from Cache"); 589 cred = Credentials.acquireTGTFromCache 590 (principal, ticketCacheName); 591 592 if (cred != null) { 593 if (!isCurrent(cred)) { 595 if (renewTGT) { 596 cred = renewCredentials(cred); 597 } else { 598 cred = null; 600 if (debug) 601 System.out.println("Credentials are" + 602 " no longer valid"); 603 } 604 } 605 } 606 607 if (cred != null) { 608 if (principal == null) { 610 principal = cred.getClient(); 611 } 612 } 613 if (debug) { 614 System.out.println("Principal is " + principal); 615 if (cred == null) { 616 System.out.println 617 ("null credentials from Ticket Cache"); 618 } 619 } 620 } 621 622 625 if (cred == null) { 626 if (principal == null) { 629 promptForName(getPasswdFromSharedState); 630 principal = new PrincipalName 631 (krb5PrincName.toString(), 632 PrincipalName.KRB_NT_PRINCIPAL); 633 } 634 if (useKeyTab) { 635 encKeys = 636 EncryptionKey.acquireSecretKeys(principal, keyTabName); 637 638 if (debug) { 639 if (encKeys != null) 640 System.out.println 641 ("principal's key obtained from the keytab"); 642 else 643 System.out.println 644 ("Key for the principal " + 645 principal + 646 " not available in " + 647 ((keyTabName == null) ? 648 "default key tab" : keyTabName)); 649 } 650 651 } 652 if (encKeys == null) { 654 promptForPass(getPasswdFromSharedState); 655 656 encKeys = EncryptionKey.acquireSecretKeys( 657 password, principal.getSalt()); 658 659 if (isInitiator) { 660 if (debug) 661 System.out.println("Acquire TGT using AS Exchange"); 662 cred = Credentials.acquireTGT(principal, 663 encKeys, password); 664 encKeys = EncryptionKey.acquireSecretKeys(password, 666 principal.getSalt()); 667 } 668 } else { 669 if (isInitiator) { 670 if (debug) 671 System.out.println("Acquire TGT using AS Exchange"); 672 cred = Credentials.acquireTGT(principal, 673 encKeys, password); 674 } 675 } 676 677 if (debug) { 679 System.out.println("principal is " + principal); 680 HexDumpEncoder hd = new HexDumpEncoder(); 681 for (int i = 0; i < encKeys.length; i++) { 682 System.out.println("EncryptionKey: keyType=" + 683 encKeys[i].getEType() + " keyBytes (hex dump)=" + 684 hd.encode(encKeys[i].getBytes())); 685 } 686 } 687 688 if (isInitiator && (cred == null)) { 690 throw new LoginException 691 ("TGT Can not be obtained from the KDC "); 692 } 693 694 } 695 } catch (KrbException e) { 696 LoginException le = new LoginException(e.getMessage()); 697 le.initCause(e); 698 throw le; 699 } catch (IOException ioe) { 700 LoginException ie = new LoginException(ioe.getMessage()); 701 ie.initCause(ioe); 702 throw ie; 703 } 704 } 705 706 private void promptForName(boolean getPasswdFromSharedState) 707 throws LoginException { 708 krb5PrincName = new StringBuffer (""); 709 if (getPasswdFromSharedState) { 710 username = (String )sharedState.get(NAME); 712 if (debug) { 713 System.out.println 714 ("username from shared state is " + username + "\n"); 715 } 716 if (username == null) { 717 System.out.println 718 ("username from shared state is null\n"); 719 throw new LoginException 720 ("Username can not be obtained from sharedstate "); 721 } 722 if (debug) { 723 System.out.println 724 ("username from shared state is " + username + "\n"); 725 } 726 if (username != null && username.length() > 0) { 727 krb5PrincName.insert(0, username); 728 return; 729 } 730 } 731 732 if (doNotPrompt) { 733 throw new LoginException 734 ("Unable to obtain Princpal Name for authentication "); 735 } else { 736 if (callbackHandler == null) 737 throw new LoginException("No CallbackHandler " 738 + "available " 739 + "to garner authentication " 740 + "information from the user"); 741 try { 742 String defUsername = System.getProperty("user.name"); 743 744 Callback[] callbacks = new Callback[1]; 745 MessageFormat form = new MessageFormat ( 746 rb.getString( 747 "Kerberos username [[defUsername]]: ")); 748 Object [] source = {defUsername}; 749 callbacks[0] = new NameCallback(form.format(source)); 750 callbackHandler.handle(callbacks); 751 username = ((NameCallback)callbacks[0]).getName(); 752 if (username == null || username.length() == 0) 753 username = defUsername; 754 krb5PrincName.insert(0, username); 755 756 } catch (java.io.IOException ioe) { 757 throw new LoginException(ioe.getMessage()); 758 } catch (UnsupportedCallbackException uce) { 759 throw new LoginException 760 (uce.getMessage() 761 +" not available to garner " 762 +" authentication information " 763 +" from the user"); 764 } 765 } 766 } 767 768 private void promptForPass(boolean getPasswdFromSharedState) 769 throws LoginException { 770 771 if (getPasswdFromSharedState) { 772 password = (char[])sharedState.get(PWD); 774 if (password == null) { 775 if (debug) { 776 System.out.println 777 ("Password from shared state is null"); 778 } 779 throw new LoginException 780 ("Password can not be obtained from sharedstate "); 781 } 782 if (debug) { 783 System.out.println 784 ("password is " + new String (password)); 785 } 786 return; 787 } 788 if (doNotPrompt) { 789 throw new LoginException 790 ("Unable to obtain password from user\n"); 791 } else { 792 if (callbackHandler == null) 793 throw new LoginException("No CallbackHandler " 794 + "available " 795 + "to garner authentication " 796 + "information from the user"); 797 try { 798 Callback[] callbacks = new Callback[1]; 799 String userName = krb5PrincName.toString(); 800 MessageFormat form = new MessageFormat ( 801 rb.getString( 802 "Kerberos password for [username]: ")); 803 Object [] source = {userName}; 804 callbacks[0] = new PasswordCallback( 805 form.format(source), 806 false); 807 callbackHandler.handle(callbacks); 808 char[] tmpPassword = ((PasswordCallback) 809 callbacks[0]).getPassword(); 810 if (tmpPassword == null) { 811 tmpPassword = new char[0]; 813 } 814 password = new char[tmpPassword.length]; 815 System.arraycopy(tmpPassword, 0, 816 password, 0, tmpPassword.length); 817 ((PasswordCallback)callbacks[0]).clearPassword(); 818 819 820 for (int i = 0; i < tmpPassword.length; i++) 822 tmpPassword[i] = ' '; 823 tmpPassword = null; 824 if (debug) { 825 System.out.println("\t\t[Krb5LoginModule] " + 826 "user entered username: " + 827 krb5PrincName); 828 System.out.println(); 829 } 830 } catch (java.io.IOException ioe) { 831 throw new LoginException(ioe.getMessage()); 832 } catch (UnsupportedCallbackException uce) { 833 throw new LoginException(uce.getMessage() 834 +" not available to garner " 835 +" authentication information " 836 + "from the user"); 837 } 838 } 839 } 840 841 private void validateConfiguration() throws LoginException { 842 if (doNotPrompt && !useTicketCache && !useKeyTab) 843 throw new LoginException 844 ("Configuration Error" 845 + " - either doNotPrompt should be " 846 + " false or useTicketCache/useKeyTab " 847 + " should be true"); 848 if (ticketCacheName != null && !useTicketCache) 849 throw new LoginException 850 ("Configuration Error " 851 + " - useTicketCache should be set " 852 + "to true to use the ticket cache" 853 + ticketCacheName); 854 if (keyTabName != null & !useKeyTab) 855 throw new LoginException 856 ("Configuration Error - useKeyTab should be set to true " 857 + "to use the keytab" + keyTabName); 858 if (storeKey && doNotPrompt && !useKeyTab) 859 throw new LoginException 860 ("Configuration Error - either doNotPrompt " 861 + "should be set to false or " 862 + "useKeyTab must be set to true for storeKey option"); 863 if (renewTGT && !useTicketCache) 864 throw new LoginException 865 ("Configuration Error" 866 + " - either useTicketCache should be " 867 + " true or renewTGT should be false"); 868 } 869 870 private boolean isCurrent(Credentials creds) 871 { 872 Date endTime = creds.getEndTime(); 873 if (endTime != null) { 874 return (System.currentTimeMillis() <= endTime.getTime()); 875 } 876 return true; 877 } 878 879 private Credentials renewCredentials(Credentials creds) 880 { 881 Credentials lcreds; 882 try { 883 if (!creds.isRenewable()) 884 throw new RefreshFailedException("This ticket" + 885 " is not renewable"); 886 if (System.currentTimeMillis() > cred.getRenewTill().getTime()) 887 throw new RefreshFailedException("This ticket is past " 888 + "its last renewal time."); 889 lcreds = creds.renew(); 890 if (debug) 891 System.out.println("Renewed Kerberos Ticket"); 892 } catch (Exception e) { 893 lcreds = null; 894 if (debug) 895 System.out.println("Ticket could not be renewed : " 896 + e.getMessage()); 897 } 898 return lcreds; 899 } 900 901 924 925 public boolean commit() throws LoginException { 926 927 932 if (succeeded == false) { 933 return false; 934 } else { 935 936 if (isInitiator && (cred == null)) { 937 succeeded = false; 938 throw new LoginException("Null Client Credential"); 939 } 940 941 if (subject.isReadOnly()) { 942 cleanKerberosCred(); 943 throw new LoginException("Subject is Readonly"); 944 } 945 946 952 953 Set privCredSet = subject.getPrivateCredentials(); 954 Set princSet = subject.getPrincipals(); 955 kerbClientPrinc = new KerberosPrincipal(principal.getName()); 956 957 if (isInitiator) { 959 kerbTicket = Krb5Util.credsToTicket(cred); 960 } 961 962 if (storeKey) { 963 if (encKeys == null || encKeys.length <= 0) { 964 succeeded = false; 965 throw new LoginException("Null Server Key "); 966 } 967 968 kerbKeys = new KerberosKey[encKeys.length]; 969 for (int i = 0; i < encKeys.length; i ++) { 970 Integer temp = encKeys[i].getKeyVersionNumber(); 971 kerbKeys[i] = new KerberosKey(kerbClientPrinc, 972 encKeys[i].getBytes(), 973 encKeys[i].getEType(), 974 (temp == null? 975 0: temp.intValue())); 976 } 977 978 } 979 if (!princSet.contains(kerbClientPrinc)) 982 princSet.add(kerbClientPrinc); 983 984 if (kerbTicket != null) { 986 if (!privCredSet.contains(kerbTicket)) 987 privCredSet.add(kerbTicket); 988 } 989 990 if (storeKey) { 991 for (int i = 0; i < kerbKeys.length; i++) { 992 if (!privCredSet.contains(kerbKeys[i])) { 993 privCredSet.add(kerbKeys[i]); 994 } 995 encKeys[i].destroy(); 996 encKeys[i] = null; 997 if (debug) { 998 System.out.println("Added server's key" 999 + kerbKeys[i]); 1000 System.out.println("\t\t[Krb5LoginModule] " + 1001 "added Krb5Principal " + 1002 kerbClientPrinc.toString() 1003 + " to Subject"); 1004 } 1005 } 1006 } 1007 } 1008 commitSucceeded = true; 1009 if (debug) 1010 System.out.println("Commit Succeeded \n"); 1011 return true; 1012 } 1013 1014 1032 1033 public boolean abort() throws LoginException { 1034 if (succeeded == false) { 1035 return false; 1036 } else if (succeeded == true && commitSucceeded == false) { 1037 succeeded = false; 1039 cleanKerberosCred(); 1040 } else { 1041 logout(); 1044 } 1045 return true; 1046 } 1047 1048 1061 public boolean logout() throws LoginException { 1062 1063 if (debug) { 1064 System.out.println("\t\t[Krb5LoginModule]: " + 1065 "Entering logout"); 1066 } 1067 1068 if (subject.isReadOnly()) { 1069 cleanKerberosCred(); 1070 throw new LoginException("Subject is Readonly"); 1071 } 1072 1073 subject.getPrincipals().remove(kerbClientPrinc); 1074 Iterator it = subject.getPrivateCredentials().iterator(); 1076 while (it.hasNext()) { 1077 Object o = it.next(); 1078 if (o instanceof KerberosTicket || 1079 o instanceof KerberosKey) { 1080 it.remove(); 1081 } 1082 } 1083 cleanKerberosCred(); 1085 1086 succeeded = false; 1087 commitSucceeded = false; 1088 if (debug) { 1089 System.out.println("\t\t[Krb5LoginModule]: " + 1090 "logged out Subject"); 1091 } 1092 return true; 1093 } 1094 1095 1098 private void cleanKerberosCred() throws LoginException { 1099 try { 1101 if (kerbTicket != null) 1102 kerbTicket.destroy(); 1103 if (kerbKeys != null) { 1104 for (int i = 0; i < kerbKeys.length; i++) { 1105 kerbKeys[i].destroy(); 1106 } 1107 } 1108 } catch (DestroyFailedException e) { 1109 throw new LoginException 1110 ("Destroy Failed on Kerberos Private Credentials"); 1111 } 1112 kerbTicket = null; 1113 kerbKeys = null; 1114 kerbClientPrinc = null; 1115 } 1116 1117 1120 private void cleanState() { 1121 1122 if (succeeded) { 1125 if (storePass && 1126 !sharedState.containsKey(NAME) && 1127 !sharedState.containsKey(PWD)) { 1128 sharedState.put(NAME, username); 1129 sharedState.put(PWD, password); 1130 } 1131 } 1132 username = null; 1133 password = null; 1134 if (krb5PrincName != null && krb5PrincName.length() != 0) 1135 krb5PrincName.delete(0, krb5PrincName.length()); 1136 krb5PrincName = null; 1137 if (clearPass) { 1138 sharedState.remove(NAME); 1139 sharedState.remove(PWD); 1140 } 1141 } 1142} 1143 | Popular Tags |