m i.c o dil
ww w. ja
va
JAAS
İsim: Osman Döner No:20221609
m
1. Giriş
dil
i.c o
JAAS (Java Authentication & Authorization Service), Java Kimlik Denetimi ve Yetkilendirme Servisi istemci ve sunucu tarafındaki Java uygulamalarımızın güvenli çalışmasını sağlamak için çözümler sunan bir uygulama geliştirme ara yüzüdür. Eski Java güvenlik yapılarında kullanılan yöntemlerde sistem kullanıcıyı taşınabilir koddan korumak üzerine odaklanmıştır. Diğer bir deyişle güvenlik yapısının temeli kodun kaynağına yani kim tarafından yazıldığı bilgisi üzerine kuruludur. Uygulamanın çalışma zamanında hangi kodun çalıştırıldığı, bu koda verilen izinler kontrol edilir; yani kullanıcı tabanlı erişim hakkı kontrolü hiçbir zaman yapılmaz. Güvenlik Politikası Dosyası(Security Policy File) adı verilen, özel bir sözdizimine sahip dosya içerisinde uygulamadaki koda özel erişim izinleri verilir. Böylece koruma altındaki bir sistem kaynağına erişim talebi geldiğinde kodun karakteristiğine(kodun nereden geldiğine, varsa dijital imzasına ve sahibine) bakılır. Politika dosyası üzerinden verilmiş haklar ile erişimi gerçekleştirmek için gerekli olan haklar karşılaştırılır. İlki ikincisini de kapsıyorsa erişime izin verilir; kapsamıyorsa istem reddedilir. Kod tabanlı denetimin yapıldığı bu sistemde kodun kime ait olduğu dışında kim tarafından çalıştırıldığı ise tamamen göz ardı edilir.
va
Bununla birlikte Java’nın çok kullanıcılı ortamlarda kullanımı oldukça yaygındır. Örneğin kurumsal bir uygulama birçok kullanıcı ile aynı anda etkileşim içindedir ve bu kullanıcılara kimliklerine göre farklı ayrıcalıklar vermelidir.
ww w. ja
Kullanıcı kimliğinin doğrulanması ve farklı kullanıcılara farklı yetkiler verilmesi gereksinimlerini karşılamak amacıyla JAAS platformu geliştirilmiştir. JAAS’ta temel düşünce eski yapıdan daha farklı olarak sistemi kullanıcı tarafından gelebilecek tehlikelere karşı korumaktır. Bu düşünce çerçevesinde kodun kaynağının ne olduğu bilgisine ek olarak hangi kullanıcı tarafından çalıştırıldığı ve kullanıcının sahip olduğu erişim hakları gibi bilgiler de göz önünde bulundurulur. Böylece sadece kod tabanlı denetim gerçekleştiren Java 2 Güvenlik Mimarisi’ne kullanıcı tabanlı denetim desteği de eklenmiştir. JAAS kimlik doğrulama(Authentication) uygulamaya kolaylıkla eklenebilir bir yapıya sahiptir. Bu da uygulamaların altta çalışan kimlik denetim teknolojilerinden bağımsız olmasını sağlar. Yeni ya da güncellenmiş bir denetim teknolojisi uygulama kodunda herhangi bir değişiklik yapılmadan eklenebilir. Uygulamalar kimlik denetim işlevini LoginContext nesnesi ile sağlarlar. Bu nesne de ilgili kimlik denetim teknolojisini belirlemek amacıyla bir Configuration nesnesinin referansını kullanır. Ya da kimlik denetim işlevini gerçekleştirmek için LoginModule nesnesine referans verir. Eğitselin bundan sonraki kısmında LoginModule’ den oturum modülü olarak bahsedilecektir. Oturum modülleri genelde kullanıcı ismi ve şifre üzerinden doğrulama yaparlar. Kodu çalıştıran kullanıcı veya servisin kimliğinin doğrulanmasının ardından JAAS’ın yetkilendirme(authorization) öğesi devreye girer. Böylece kritik kaynaklara erişim kullanıcı düzeyinde kontrol edilir. J2SDK’nın 1.3 ve daha önceki versiyonlarında 2
m
erişim kontrolleri kodun sahibi ve kodun nereden işletildiği(CodeSource nesnesinde ele alınan) bilgileri üzerinden yapılıyorken; 1.4 versiyonundaki JAAS eklentisiyle erişim kontrolleri CodeSource dışında kodu hangi kullanıcı veya servisin çalıştırdığı(Subject nesnesi tarafından ele alınır) bilgisini de temel alır.
i.c o
JAAS daha önceleri seçeneğe bağlı bir paket olarak sunulurken kullanıcılar üzerinde erişim yapmak amacıyla 1.4 sürümüyle birlikte çekirdek yapıya dâhil edilmiştir.
2. Çekirdek Sınıf ve Ara yüzler
JAAS’ın çekirdek sınıf ve ara yüzleri 3 farklı sınıfa ayrılır:
dil
Ortak Sınıflar: Subject, Principal ve credential(herhangi bir nesne olabilir) Authentication(Kimlik Denetimi) sınıfları ve ara yüzleri: LoginContext,
LoginModule, CallbackHandler, Callback
sınıfları: Policy, AuthPermission,
va
Authorization(Yetkilendirme) PrivateCredentialPermission
2.1. Ortak Sınıflar
ww w. ja
Ortak sınıflar JAAS’ın hem kimlik denetim(authentication) hem de yetkilendirme(authorization) öğeleri tarafından kullanılan sınıflardır. Bu bağlamda en önemli sınıf kullanıcı konumundaki bir kişi veya servisle ilgili bilgilerin ele alındığı javax.security.auth.Subject sınıfıdır.
2.1.1. Subject
Kaynaklara erişime yetki vermek için uygulamaların öncelikle istemin geldiği kaynağın kimlik denetimini yapması ve kimliğini onaylaması gerekir. JAAS bir istemin kaynağını tanımlamak üzere subject terimini kullanır. Eğitsel kapsamında terimin karşılığı olarak “özne” kelimesi düşünülmüştür. Özne bir kişi veya servis gibi herhangi bir varlık olabilir. Kimlik denetimi işleminden geçtikten sonra ilgili özelliklerle (Principal nesneleri tarafından ele alınan) ilişkilendirilir. Türkçe karşılığı “temel nitelik” olarak düşünülebilir. Subject nesnesi bahsedildiği üzere özne bilgilerinin ele alındığı yapıdır. Bir Subject nesnesi birçok Principal nesnesi ile ilişkili olabilir. Örneğin bir kişi bir isim temel niteliği(“Osman Döner”) taşıdığı gibi, kimlik numarası
3
m
temel niteliğine de (123-45-678) sahip olabilir. İlgili Principal nesneleri özneyi diğerlerinden ayırt etmeye yarar.
i.c o
Subject nesneleri güvenlikle ilişkili özelliklere de sahip olabilirler. Bu özellikler credentials(kimliği gösteren nitelikler) olarak adlandırılır. Credential sınıfları öznenin güvenlikle ilişkili niteliklerini simgelerler. Gizliliği önemli olan nitelikler(örneğin kullanıcı şifresi) özel bir kümede saklanır.(private credential Set) Paylaşımı amaçlanan nitelikler ise ortak erişime açık bir kümede(public credential Set) saklanır. Farklı credential kümelerine erişim için farklı yetkilere ihtiyaç vardır.
Subject nesnelerini yaratmak için aşağıdaki yapıcı metotlar(constructor) kullanılır. public Subject();
dil
public Subject(boolean readOnly, Set principals, Set pubCredentials, Set privCredentials);
İlk metot ile boş ama adres değeri atanmış credential ve Principal kümeleri oluşturulur. İkincisi ise belirtilen kümelerle(Set) bir Subject nesnesi oluşturur. Ayrıca Subject nesnesini salt okunur olarak yaratacak bir parametre de mevcuttur. Salt okunur bir Subject nesnesinde Principal ve credential kümeleri değişmezdir.
va
Uygulama geliştiren kişi bir Subject nesnesi yaratmak zorunda değildir. Uygulama bir LoginContext nesnesi yaratır ve parametre olarak Subject nesnesini geçmezse, LoginContext kendisi boş bir Subject nesnesi yaratır. Bununla ilgili detaylar kimlik denetimiyle ilgili sınıflar incelenirken irdelenecektir.
ww w. ja
Subject nesnesi yaratılırken salt okunur olduğu belirtilmemişse aşağıdaki metot ile salt okunur hale getirilebilir: public void setReadOnly();
salt okunur konumundayken Principal ve credential kümelerinde yapılacak ekleme veya silme girişimleri IllegalStateException uyarısına neden olacaktır. Subject
Aşağıdaki metot ise Subject nesnesinin salt okunur olup olmadığını test etmemizi sağlar: public boolean isReadOnly();
nesnesiyle ilişkili Principal nesneleri kümesine erişmek için aşağıdaki metotlar kullanılır: Subject
public Set getPrincipals(); public Set getPrincipals(Class c);
4
i.c o
m
İlk metot Subject nesnesiyle ilişkili tüm Principal nesnelerini içeren bir kümeyi geri döndürür. İkinci metot ise belirtilen sınıfın nesnesi olan Principal’ları içeren kümeyi geri döndürür. Subject nesnesi Principal nesnesine sahip değilse geriye boş bir küme döner. Ortak erişime açık(public) credential nesnelerine erişmek için aşağıdaki metotlar kullanılır: public Set getPublicCredentials(); public Set getPublicCredentials(Class c);
Çalışma prensibi getPrincipals metodunda olduğu gibidir. İlk metot tüm credential nesnelerini bir kümede gönderirken, ikincisi belirtilen sınıfın nesnelerini gönderir. Gizli(private) credential nesnelerine erişim aşağıdaki metotlar ile sağlanır:
dil
public Set getPrivateCredentials(); public Set getPrivateCredentials(Class c);
Yukarıdaki kümeler üzerinde işlem yapabilmek için java.Util.Set sınıfına ait metotlar kullanılır. Aşağıdaki örnek ekleme işleminin nasıl gerçekleştirildiğini gösterir:
. . .
va
Subject subject; Principal principal; Object credential;//Herhangi bir nesne olabileceğini söyledik
ww w. ja
// Subject nesnesine bir Principal ve credential ekle subject.getPrincipals().add(principal); subject.getPublicCredentials().add(credential);
Not: Kümeler üzerinde işlem yapabilmek için AuthPermission nesnesi ile yetkilendirmenin gerçekleştirilmiş olması gerekir. AuthPermission nesnesi denetim amaçlı kullanılan bir sınıftır. Bir hedef isminden oluşur. Bu hedef ismi gerçekleştirilmek istenen işlemi ifade eder. Principal ve credential kümeleri üzerinde işlem yapabilmek için modifyPrincipals", "modifyPublicCredentials" ve "modifyPrivateCredentials” hedef isimleriyle AuthPermission
nesnesinin yaratılmış olması gerekir. Bunu gerçekleştirmek için aşağıdaki şekliyle güvenlik politikası(security policy) dosyamıza eklenmelidir. grant codebase "file:/users/petitp/simple/simple_module.jar" { permission javax.security.auth.AuthPermission "modifyPrincipals"; permission javax.security.auth.AuthPermission "modifyPublicCredentials"; permission javax.security.auth.AuthPermission "modifyPrivateCredentials"; };
5
m
Gizli credential nesnelerinin oluşturduğu kümenin elemanlarına erişebilmek için javax.security.auth.PrivateCredentialPermission ile ilgili yetkilendirmenin gerçekleştirilmiş olması gerekir. Bu sınıf gizli olarak tanımlanmış credential nesnelerine izinsiz erişimi engellemek amacıyla kullanılır.
i.c o
Subject nesnesi bir AccessControlContext(aşağıdaki doAs ve doAsPrivileged metotlarını inceleyin) nesnesiyle ilişkilendirilmiş olabilir. Aşağıdaki metot belirtilen AccessControlContext ile ilişkili Subject nesnesini döndürür veya herhangi bir Subject nesnesiyle ilişkili değilse geriye null döndürür. public static Subject getSubject(final AccessControlContext acc); Subject.getSubject metodunu çağırabilmek AuthPermission üzerinden yetki vermek gerekir.
için hedef ismi “getSubject” olan bir
dil
Subject sınıfı ek olarak java.lang.Object sınıfından aşağıdaki metotları içerir:
va
public boolean equals(Object o); public String toString(); public int hashCode();
2.1.1.1. doAs Metodouyla Subject Nesnesinin İstenilen Bir İşlevi Gerçekleştirmesi
ww w. ja
Bir Subject nesnesi olarak belirli bir işlevi gerçekleştirmek için aşağıdaki statik metotlar çağırılabilir: public static Object doAs(final Subject subject, final java.security.PrivilegedAction action);
doAs metodu ilk olarak özneyi geçerli Thread’e ait AccessControlContext nesnesiyle ilişkilendirir; ardından işlevi(action) gerçekleştirir. Böylece işlevin belirtilen özne tarafından gerçekleştirilmesi sağlanır. doAs metodunun çağırılması için AuthPermission ile “doAs” hedef ismi üzerinden yetki verilmesi gerekir. 2.1.1.2. Subject.doAs Örneği
Örneğin “Osman” isminde bir kullanıcının LoginContext nesnesi tarafından kimliğinin doğrulandığını varsayalım. Sonuç olarak bir com.ibm.security.Principal (temel nitelik bu durumda ‘Osman’ oluyor) nesnesiyle ilişkili bir Subject nesnesi oluşturulmuştur. Ek olarak sıradaki yapının erişim kontrol politikasına dâhil edildiğini varsayalım.
6
m
// Osman’a x.txt’e dosyasına yazma izni veriliyor grant Principal com.ibm.security.Principal "Osman" {
permission java.io.FilePermission "x.txt", "read";
Aşağıdaki örnek uygulama programıdır:
i.c o
};
class ExampleAction implements java.security.PrivilegedAction { public Object run() { java.io.File f = new java.io.File("x.txt");
}
dil
}
// sıradaki metot güvenlik kontrolü yapar if (f.exists()) { System.out.println("File x.txt exists"); } return null;
public class Example1 { public static void main(String[] args) {
va
// “Osman” öznesinin kimliğini doğrula // LoginContext kısmında açıklanacak
Subject osman; // Kimlik doğrulama sürecinde yaratılan Subject’e osman’ı ata
}
ww w. ja
}
// ExampleAction işlevini Osman kimliğiyle gerçekleştir Subject.doAs(osman, new ExampleAction());
Çalışma sürecinde ExampleAction içinde f.exists() ile güvenlik kontrolü yapılır. ExampleAction Osman kimliğiyle çalıştırıldığından ve politika dosyası içinde ilgili izni verdiğimiz için güvenlik kontrolünden geçecektir. Eğer gerekli yetki verilmemiş olsaydı SecurityException uyarısı verilecekti. 2.1.1.3. doAsPriveleged Metodu
Aşağıdaki metotlar da işlevin(action) belirli bir özne(subject) tarafından gerçekleştirilmesini sağlar.
7
m
public static Object doAsPrivileged( final Subject subject, final java.security.PrivilegedAction action, final java.security.AccessControlContext acc);
i.c o
public static Object doAsPrivileged( final Subject subject, final java.security.PrivilegedExceptionAction action, final java.security.AccessControlContext acc) throws java.security.PrivilegedActionException;
dil
Bu metotları çağırabilmek için ilgili iznin AuthPermission ile “doAsPriveleged” hedef ismi üzerinden verilmiş olması gerekir. 2.1.1.4. doAs ve doAsPriveleged
va
doAsPriveleged metotları doAs metotları gibi çalışır. Tek fark sağlanan Subject nesnesini geçerli Thread’in AccessControlContext nesnesiyle ilişkilendirmek yerine argüman olarak sağlanan AccessControlContext nesnesiyle ilişkilendirir. Bu yolla işlevler diğerinden farklı olarak AccessControlContext nesnesiyle sınırlandırılabilir.
ww w. ja
Bir AccessControlContext nesnesi yaratıldığı andan itibaren işletilen tüm kodun bilgisini tutar. Bu bilgi kodun işletildiği konum ve koda güvenlik politikası(security policy) tarafından verilen izinler gibi detayları içerir. Güvenlik kontrolünden geçebilmek için politika AccessControlContext tarafından referans gösterilen her bir koda gerekli izinleri vermiş olmalıdır. doAsPriveleged metoduna parametre olarak geçilen AccessControlContext nesnesi null ise işlev(action) ayrı bir AccessControlContext nesnesi ile sınırlandırılmaz. Bunun faydalı olabileceği bir duruma örnek sunucu ortamıdır. Bir sunucu gelen birçok istemi doğrulayabilir ve her bir istem için ayrı birer doAs metodu çağırabilir. Her bir doAs metoduna sunucunun o anki AccessControlContext nesnesinin kısıtlamalarından bağımsız başlamak isteniyorsa bu metot kullanılabilir. Sunucu bunu gerçekleştirmek için doAsPriveleged metoduna AccessControlContext nesnesini null olarak geçer.
2.1.2. Principal
Daha önce de vurguladığımız gibi Principal nesneleri kimlik doğrulama işleminden geçtikten sonra ilgili Subject nesnesiyle ilişkilendirilir. Subject kimliğini ifade eden temel nitelikler gibi düşünülebilirler.(isim, numara gibi) java.security.Principal ve java.io.Serializable ara yüzlerini gerçekleştirmiş olmalıdırlar. Principal nesnelerini güncellemek için kullanılan metotlar Subject kısmında açıklanmıştır.
8
m
2.1.3. Credential
i.c o
Subject kısmında da ifade ettiğimiz gibi Subject nesnesiyle ilişkilendirilmiş, güvenlikle ilgili nesnelere credential ismi verilmiştir. Gizli(private) ve açık(public) credential sınıfları çekirdek JAAS kütüphanesinin öğeleri değillerdir. Credential nesnesi herhangi bir türde olabilir. Bununla birlikte kendi yarattığımız sınıfların gerçekleştirimini credential sınıflarına özel ara yüzlerle yapabiliriz. Bu ara yüzler aşağıda açıklanmıştır: 2.1.3.1. Refreshable
boolean isCurrent();
dil
javax.security.auth.Refreshable ara yüzü credential nesnesinin kendini yenilemesini sağlar. Örneğin sınırlı bir yaşam döngüsü olan bir credential nesnesi bu ara yüzü kullanarak geçerli olduğu zaman periyodunun yenilenmesini(refresh) sağlayabilir. İki soyut metodu vardır:
Credential nesnesinin o anda geçerli olup olmadığı bilgisini döndürür. void refresh() throws RefreshFailedException;
va
Credential nesnesinin geçerliliğini genişletir veya günceller. Metodun gerçekleştiriminde AuthPermission(“refreshCredential”) güvenlik kontrolü yapılmalıdır. Böylece metodu çağıran kişinin yenileme işlemi için yetkisi olup olmadığı kontrol edilir.
ww w. ja
2.1.3.2. Destroyable
javax.security.auth.Destroyable ara yüzü credential içeriğinin yok edilmesini sağlar. İki soyut metodu vardır: boolean isDestroyed();
Credential nesnesinin yok edilip edilmediği bilgisini geri döndürür. void destroy() throws DestroyFailedException;
Credential ile ilişkili bilgiyi siler. Bu credential nesnesine ilişkin yapılacak sonraki çağrılar IllegalStateException uyarısına neden olur. Metodun gerçekleştiriminde AuthPermission(“destroyCredential”) ile yetki verilmiş olmalıdır. Böylece metodu çağıran kişinin silme işlemi için yetkisi olduğu belirtilir.
9
JAAS’ın Kimlik Denetimi(authentication) amaçlı kullanımı
m
2.2.
i.c o
Bir özne(kullanıcı veya servis) üzerinde kimlik doğrulama işlemi yapmak için aşağıdaki adımlar gerçekleştirilir:
dil
1. Uygulama bir LoginContext nesnesi yaratır. 2. LoginContext uygulamaya özgü olarak yapılandırılmış tüm LoginModule’lerini yüklemek için bir Configuration’ı referans gösterir. Configuration belli bir uygulama için hangi LoginModule’lerinin kullanılması gerektiği ve kullanımlarının hangi sırada olması gerektiği bilgisini içerir. 3. Uygulama LoginContext nesnesinin login metodunu çağırır. 4. login metodu yüklenen tüm LoginModule’lerini çağırır. Her bir modül kullanıcıyı doğrulamak için girişimde bulunur. 5. Kimlik doğrulama işleminin başarıya ulaşması durumunda LoginModule’leri ilgili Principal ve Credential nesnelerini kimliği doğrulanan kullanıcıyı simgeleyen Subject nesnesiyle ilişkilendirir. 6. LoginContext kimlik doğrulama sonucunu uygulamaya bildirir. 7. Sonuç başarılıysa, uygulama LoginContext nesnesinden Subject nesnesini alır.
va
Kimlik doğrulama sınıfları aşağıda açıklanmıştır.
2.2.1. LoginContext
sınıfı kullanıcı kimliklerinin doğrulanması için temel metotları içerir ve altta çalışan kimlik doğrulama teknolojisinden bağımsız uygulama geliştirme imkanı sağlar. LoginContext, uygulamaya özgü kimlik denetim servislerini(LoginModule’leri) belirlemek amacıyla yukarıda da belirtildiği gibi Configuration nesnesine danışır. Bu nedenle uygulamanın kendisinde herhangi bir değişiklik yapmadan, uygulama altına farklı modüller eklenebilir.
ww w. ja
javax.security.auth.login.LoginContext
LoginContext
dört yapıcı metot(constructor) içerir:
public LoginContext(String name) throws LoginException; public LoginContext(String name, Subject subject) throws LoginException; public LoginContext(String name, CallbackHandler callbackHandler) throws LoginException; public LoginContext(String name, Subject subject, CallbackHandler callbackHandler) throws LoginException;
10
i.c o
m
Hepsinin ortak bir parametresi bulunuyor: name. Bu argüman LoginContext tarafından oturum konfigürasyonuna bir indeks değeri olarak geçilir. Configuration bu indeksi kullanarak hangi oturum modüllerinin LoginContext’i yaratan uygulama için yapılandırıldıklarını belirler. Subject nesnesini parametre olarak almayanlarda ise yeni bir Subject nesnesi yaratılır. Null değeri taşıyan parametrelere izin verilmez. LoginContext nesnesi yaratabilmek için “createLoginContext.
" hedef ismi üzerinden bir AuthPermission ile yetki verilmesi gerekir. Buradaki “” LoginContext’i yaratırken kullandığımız isim ve aynı zamanda yapılandırma nesnesinin(Configuration) kullandığı indeks değeridir. CallbackHandler nesnesinin ne olduğu ileriki bölümlerde açıklanacaktır. Aslen kimlik doğrulama işlemi aşağıdaki metot ile gerçekleştirilir:
dil
public void login() throws LoginException;
Bu metodun çağırılmasıyla birlikte kimlik doğrulama için uygulamaya ilişkin tüm oturum modülleri(LoginModule) çağırılır. Doğrulama başarıyla sonuçlanırsa Subject(bundan sonra kullanıcıya ilişkin nitelikleri-Principal, Credential-taşıyacak olan) nesnesine aşağıdaki metot aracılığıyla ulaşılır.
va
public Subject getSubject();
Sözü geçen kullanıcı veya servisin oturumunu sonlandırmak ve buna ilişkin doğrulanmış Principal ve Credential nesnelerini uygulamadan silmek için aşağıdaki metot çağırılır. public void logout() throws LoginException;
ww w. ja
Sıradaki kod bir servisi doğrulamak ve ardından oturumunu sonlandırmak için gerekli işlemleri göstermektedir. // LoginContext yeni bir Subject yaratır LoginContext lc = new LoginContext("confEntry"); //confEntry(yapılandırma kaydı) yapılandırma dosyasında ilgili modülleri //belirlemek aracılığıyla kullanılır try { // Subject kimliği doğrulanır lc.login();//Doğrulanamazsa LoginException uyarısı yapılır System.out.println("authentication successful");
// Kimlik doğrulamadan geçen Subject ele alınır Subject subject = lc.getSubject(); ...
}
// Gerekli işlemler yapıldıktan sonra oturum sonlandırılır lc.logout();
11
i.c o
m
catch (LoginException le) { System.err.println("authentication unsuccessful: " + le.getMessage()); }
2.2.2. LoginModule
Bu ara yüz, geliştiricilere bir uygulamanın arka planına eklenebilecek farklı kimlik doğrulama teknolojilerinin gerçekleştirilmesi imkânı sunar. Örneğin bir LoginModule kullanıcı ismi/şifre tabanlı bir form aracılığıyla kimlik doğrulama işlemini gerçekleştirebilir. Diğer modüller ise donanım tabanlı denetimler (örneğin kişiye özel kartlar aracılığıyla) yapabilir.
va
2.2.3. CallbackHandler
dil
Not: Bir uygulama programcısıysanız oturum modüllerinin nasıl çalıştığını bilmek zorunda değilsiniz. Tüm bilmeniz gereken uygulamanızı nasıl yazmak gerektiği ve yapılandırma bilgisini(oturum yapılandırma dosyası gibi) düzenlemektir. Böylece uygulama kullanıcıyı kimlik denetiminden geçirirken yapılandırmada belirtilen modülü(LoginModule) kullanır.
ww w. ja
Bazı durumlarda oturum modülü gerekli denetim bilgisini edinmek için kullanıcı ile iletişim kurmak zorundadır. Oturum modülleri bu amaçla javax.security.auth.callback.CallbackHandler ara yüzünü kullanırlar. Uygulamalar bu ara yüzün gerçekleştirimini yaparak LoginContext’e parametre olarak geçerler. LoginContext de işleyiciyi(handler) daha alttaki modüllere yönlendirir. Modül CallbackHandler’ı kullanıcıdan gerekli girdiyi(şifre veya kart numarası gibi) almak veya kullanıcıya gerekli bilgiyi(durum bilgisi gibi) sağlamak amacıyla kullanılır. Uygulamanın CallbackHandler’ı belirtmesini sağlayarak, daha alttaki oturum modüllerinin uygulamaların kullanıcılarla kurduğu farklı etkileşim yollarından bağımsız olmasını sağlar. Örneğin bir CallbackHandler gerçekleştirimi kullanıcı ara yüzü uygulaması için bir pencere gösteriminde rol oynayabilir ve kullanıcı bu pencere üzerinden uygulamanın gereksinim duyduğu bilgiyi girebilir. Diğer yandan kullanıcı ara yüzü olmayan bir uygulama için CallbackHandler komut satırından bilgi isteminde bulunabilir. CallbackHandler tek metodu olan bir ara yüzdür. void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException;
Oturum modülü ara yüzün handle metoduna bir dizi ilgili Callback nesnesi geçer. Örneğin kullanıcı ismi bir NameCallback, şifre için PasswordCallback parametre olarak geçilir ve CallbackHandler kullanıcıyla gereksinim duyulan etkileşimi gerçekleştirir; 12
m
Callback nesnelerine de etkileşim sonucunda gerekli değerleri atar. Örneğin NameCallback’i işlemek için CallbackHandler kullanıcıdan isim için istemde bulunur, kullanıcının girdiği değeri alır ve değer atama işlemi için NameCallback’in setName
metodunu çağırır.
i.c o
javax.security.auth.callback paketi birçok gerçekleştirimiyle birlikte Callback ara yüzünü içerir. Yukarıda görüldüğü gibi oturum modülü handle metodunu
çağırırken bu gerçekleştirimlerden oluşan bir diziyi parametre olarak geçer; böylece kullanıcıdan gereksinim duyulan bilgiler için istemde bulunulmuş olur.
2.2.4. Örnek Uygulama
Bu kısımda üç ayrı dosya örnek olarak incelenecektir.
dil
SampleAcn.java: Örnek uygulama sınıfını ve kullanıcı girdisini ele alan bir MyCallBackHandler sınıfını içerir.Bu dosyadaki kod eğitsel kapsamında esas anlaşılması gereken koddur. Diğer dosyalar uygulama tarafından dolaylı olarak kullanılacak dosyalardır.
va
SampleLoginModule.java: Bu sınıf eğitselde ileriki aşamalarda değineceğimiz oturum yapılandırma dosyası tarafından belirtilen ve arka planda arzu edilen kimlik denetim işlevini gerçekleştiren sınıftır. Sözü geçen kimlik denetim işlevi kullanıcı tarafından girilen kullanıcı ismi ve şifre verilerini doğrular.
ww w. ja
SamplePrincipal.java: java.security.Principal ara yüzünü gerçekleştiren sınıftır. Oturum modülü(SampleLoginModule) tarafından kullanılır. 2.2.4.1. SampleAcn.java
Eğitsel kapsamında uygulama kodu tek bir kaynak dosya içerisinde bulunmaktadır, SampleAcn.java. Bu dosya da kendi içinde iki sınıfı içerir: SampleAcn sınıfı MyCallbackHandler sınıfı 2.2.4.2. SampleAcn Sınıfı
metodu kimlik denetim işlevini gerçekleştirir ve doğrulamanın başarıya ulaşıp ulaşmadığını geri bildirir. main
Kullanıcının doğrulanması işlemi iki adımdan oluşan çok basit bir süreçtir:
13
i.c o
LoginContext oluşturulması:
m
1. Bir LoginContext yaratılır. 2. Yaratılan LoginContext’in login metodu çağırılır.
Bir kullanıcıyı doğrulamak için öncelikle LoginContext nesnesine ihtiyaç duyulur. Aşağıda bu işlem için kullanılan basit bir yol gösterilmektedir.
dil
import javax.security.auth.login.*; . . . LoginContext lc = new LoginContext(, );
Aşağıdaki yöntemde ise eğitsel kapsamında kullanılan yol örneklenmiştir.
va
import javax.security.auth.login.*; . . . LoginContext lc = new LoginContext("Sample", new MyCallbackHandler());
1. JAAS oturum yapılandırma dosyasında yer alan bir kaydın ismi:
ww w. ja
LoginContext’in uygulamaya özel olarak oturum yapılandırma dosyası içerisinde aradığı kaydın ismidir. Bu kayıt arka planda çalışan isteğe bağlı kimlik denetim teknolojilerini uygulayan sınıfları belirtir. Bu sınıflar LoginModule ara yüzünü gerçekleştirmiş olmalıdırlar.
Örnek kodumuzda kendi SampleLoginModule dosyamızı kullanacağız. Bu oturum modülü kullanıcının girdiği kullanıcı ismi ve şifre aracılığıyla denetim işlevini gerçekleştirir.
Örneğimiz için oturum yapılandırma dosyasında(sample jaas.config) kullandığımız kaydın ismi Sample’dır. Bu nedenle LoginContext’i yaratırken parametre olarak geçtiğimiz isim de budur. “sample jaas.config” dosyasının görünümü aşağıdaki gibidir.
14
m
/** JAAS örnek uygulamamız için oturum yapılandırması **/
i.c o
Sample { sample.module.SampleLoginModule required debug=true; }; //Böylece LoginContext’e parametre olarak “Sample” // geçildiğinde ilgili oturum modülü olarak //“SampleLoginModule” yüklenir.
2. CallbackHandler nesnesi
dil
Bir LoginModule kullanıcıyla iletişim kurma gereği duyduğunda, örneğin kullanıcı ismi ve şifre için istemde bulunurken bunu doğrudan gerçekleştirmez. Bunun nedeni kullanıcıyla iletişimin birçok yolunun olması ve oturum modüllerinin kullanıcıyla etkileşimin farklı türlerinden bağımsız olmasının istenmesidir. Bunu sağlamak amacıyla LoginModule kullanıcıyla etkileşimi sağlamak(kullanıcı ismi ve şifre için istemde bulunmak gibi) için CallbackHandler ara yüzünü aracı olarak kullanır. Böylece uygulama kodunu değiştirmeden kullanıcıyla etkileşim kurma yolunda değişikliğe gidilebilir. LoginContext yaratılırken ilgili parametresini değiştirmek yeterlidir.
ww w. ja
va
Bir önceki sayfada görüldüğü gibi CallbackHandler ara yüzünü gerçekleştiren bir nesne LoginContext yapıcı metoduna parametre olarak geçilir. LoginContext de nesneyi oturum modülüne(bizim örneğimizde SampleLoginModule) yönlendirir. Genelde uygulamalar kendi özel CallbackHandler gerçekleştirimlerine sahiptirler. Standart olarak iki CallbackHandler gerçekleştirimi Java tarafından sağlanmaktadır, TextCallbackHandler ve DialogCallbackHandler. Bu eğitsel kapsamında komut satırı üzerinden ileti göndermeye ve kullanıcı girdisi almaya yarayan TextCallbackHandler kullanılabilirdi. Ancak birçok uygulamanın kendi özel gerçekleştirimini kullandığını göz önünde bulundurarak biz de kendi MyCallbackHandler sınıfımızı oluşturduk. Bu sınıfla ilgili detaylar sonraki kısımlarda açıklanacaktır. LoginContext’in login metodunun çağırılması süreci
lc değişken ismiyle LoginContext nesnemizi yarattıktan sonra kimlik doğrulama işlevini gerçekleştirmek için login metodunu aşağıdaki gibi çağırırız. lc.login();
LoginContext yeni ve henüz içerik ataması yapılmamış bir javax.security.auth.Subject nesnesi(kimlik denetimi yapılan kişi veya servise karşılık gelir) yaratır. Bununla birlikte yapılandırma dosyasında tanımlı ilgili LoginModule nesnesini(örneğimizde SampleLoginModule) yaratır; bu oturum modülünün ilk değer atamasını da yaratılan yeni Subject nesnesiyle ve MyCallbackHandler nesnesiyle yapar.
15
i.c o
m
Daha sonra login metodu kimlik doğrulama ve oturum açma işlemlerini gerçekleştirmek için SampleLoginModule içindeki metotları çağırır. SampleLoginModule kullanıcı tarafından kullanıcı ismi ve şifre verilerini almak için MyCallbackHandler sınıfını kullanacaktır. Ardından SampleLoginModule girilen verilerle beklenen verileri kıyaslar.
Kimlik denetimi başarıya ulaşırsa Subject nesnesini kullanıcı kimliğini simgeleyen bir Principal ile ilişkilendirir. Sözü geçen Principal da SamplePrincipal sınıfımızın bir olgusudur. SamplePrincipal sınıfı bunun için java.security.Principal ara yüzünü gerçekleştirmiş olmalıdır. Subject sınıfı ve Principal ara yüzü ortak sınıf ve ara yüzler anlatılırken açıklanmıştır.
dil
Kimliğin doğrulanmasıyla birlikte uygulama LoginContext nesnesinin getSubject metodunu çağırarak kimliği doğrulanmış Subject nesnesine erişebilir. Eğitsel kapsamında buna gerek duyulmamıştır. 2.2.4.3. SampleAcn sınıfının kodu package sample;
va
import javax.security.auth.login.*; // . . . MyCallbackHandler için gerekli diğer import deyimleri
ww w. ja
/** * Bu uygulama kimlik denetim işlemini gerçekleştirerek * başarılı olup olmadığını geri bildirir * */ public class SampleAcn {
/** * Kullanıcı kimliğini doğrular * */ public static void main(String[] args) { // // // //
Kimlik denetimi için gerekli LoginContext olgusunu yarat Hangi oturum modülünü kullanması gerektiğini “Sample” kayıt ismiyle JAAS yapılandırma dosyasında belirt. Aynı zamanda hangi CallbackHandler’ı kullanacağını belirt
LoginContext lc = null; try {
lc = new LoginContext("Sample", new MyCallbackHandler());
} catch (LoginException le) { System.err.println("Cannot create LoginContext. "
16
m
+ le.getMessage()); System.exit(-1);
} catch (SecurityException se) { System.err.println("Cannot create LoginContext. " + se.getMessage()); System.exit(-1);
i.c o
}
// kullanıcı oturum açmak için 3 kere girişimde bulunabilir int i; for (i = 0; i < 3; i++) { try {
dil
// kimlik denetimini yap lc.login();
// aykırı durum olmadan metottan geri dönülürse // kimlik doğrulama başarıya ulaşmıştır break; } catch (LoginException le) {
}
ww w. ja
}
va
System.err.println("Authentication failed:"); System.err.println(" " + le.getMessage()); try { Thread.currentThread().sleep(3000); } catch (Exception e) { // ... }
// 3 girişim de başarısız olduysa if (i == 3) { System.out.println("Sorry"); System.exit(-1); }
System.out.println("Authentication succeeded!");
}
}
2.2.4.4. MyCallbackHandler Sınıfı
Bazı durumlarda oturum modülü(LoginModule) kimlik denetimi için kullanıcı tarafından girilecek verilere ihtiyaç duyar. Bu amaçla oturum modülleri javax.security.auth.callback.CallbackHandler ara yüzünü kullanırlar. Uygulamayı geliştirenler Java içinde standart gelen CallbackHandler gerçekleştirimlerini kullanabilecekleri gibi kendi geliştirdikleri sınıfları da kullanabilirler. Uygulama
17
m
LoginContext yaratılırken yukarıdaki örnekte görüldüğü gibi ilgili CallbackHandler gerçekleştirimini parametre olarak geçer. LoginContext nesnesi de parametreyi ilgili LoginModule nesnelerine yönlendirir.
i.c o
Eğitsel kapsamında kendi CallbackHandler gerçekleştirimimizi oluşturacağız. Bu sınıfta SampleAcn.java dosyası içerisinde yukarıda kodunu yazdığımız SampleAcn sınıfıyla birlikte yer alacak. CallbackHandler tek metot içeren bir ara yüzdür.
void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException;
dil
LoginModule javax.security.auth.callback.Callback nesnelerinden oluşan bir diziyi parametre olarak handle metoduna geçer. Örneğin bunlar kullanıcının ismine karşılık bir NameCallback ve şifresine karşılık gelen bir PasswordCallback nesneleri olabilir. CallbackHandler kullanıcı ile etkileşimi sağlayarak, kullanıcı tarafından girilen veriler doğrultusunda bu nesnelere uygun değerleri atar.
va
MyCallbackHandler handle metodunun yapısı aşağıdaki gibidir: public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
ww w. ja
//Tüm dizi taranıyor; //Dizideki herbir elemanın türüne gore //kullanıcıyla uygun etkileşim sırayla sağlanıyor for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] TextOutputCallback türündeyse) { // uygun iletiyi kullanıcıya gönder . . .
} else if (callbacks[i] NameCallback türündeyse) { // kullanıcıdan kullanıcı ismi için istemde bulun . . .
} else if (callbacks[i] PasswordCallback türündeyse) { // kullanıcıdan şifre için istemde bulun . . .
}
} else { throw new UnsupportedCallbackException (callbacks[i], "Unrecognized Callback"); }
}
18
üç türde Callback nesnesini ele alır: kullanıcıdan kullanıcı ismi çin istemde bulunma amacıyla NameCallback, şifre için istemde bulunmak amacıyla PasswordCallback ve herhangi bir hata oluştuğunda geribildirim yapmak veya SampleLoginModule tarafından gönderilecek farklı mesajları kullanıcıya iletmek amacıyla TextOutputCallback.
i.c o
m
MyCallbackHandler
Gönderilecek ileti TextOutputCallback’in getMessage metoduyla, türü ise getMessageType ile belirlenir ve kullanıcıya ileti System.out fonksiyonuyla gösterilir. Aşağıda TextOutputCallback nesnesini nasıl ele alındığı gösterilmektedir:
if (callbacks[i] TextOutputCallback türündeyse) {
// iletiyi türüne uygun olarak göster TextOutputCallback toc = (TextOutputCallback)callbacks[i];
dil
switch (toc.getMessageType()) {
case TextOutputCallback.INFORMATION: System.out.println(toc.getMessage()); break;
va
case TextOutputCallback.ERROR: System.out.println("ERROR: " + toc.getMessage()); break;
case TextOutputCallback.WARNING: System.out.println("WARNING: " + toc.getMessage()); break;
ww w. ja
default: throw new IOException("Unsupported message type: " + toc.getMessageType());
}
handle metodu bunun dışında kullanıcıdan kullanıcı ismi için istemde bulunarak nesnesini ele alır. Kullanıcıya bu istem System.err fonksiyonu ile bildirilir. Ardından kullanıcı tarafından girilen veri SampleLoginModule tarafından kullanılmak üzere setName metodu ile NameCallback nesnesine kaydedilir. NameCallback
} else if (callbacks[i] NameCallback türündeyse) { //Kullanıcı ismi için istemde bulun NameCallback nc = (NameCallback)callbacks[i];
//İstem iletisini kullanıcıya ilet System.err.print(nc.getPrompt()); System.err.flush();
19
i.c o
m
//Kullanıcı tarafından girilen veriyi SampleLoginModule //tarafından kullanılmak üzere kaydet nc.setName((new BufferedReader (new InputStreamReader(System.in))).readLine());
Kullanıcıdan şifre için istemde bulunma da benzer biçimde gerçekleştirilir. Bu sefer gerçekleştirimi olarak PasswordCallback nesnesi ve şifreyi kaydetmek için bu nesnenin setPassword metodu kullanılır. CallbackHandler
} else if (callbacks[i] PasswordCallback türündeyse) {
// Gizli bilgi için kullanıcıdan istemde bulun PasswordCallback pc = (PasswordCallback)callbacks[i];
dil
System.err.print(pc.getPrompt()); System.err.flush();
//Kullanıcı tarafından girilen veriyi oku pc.setPassword(readPassword(System.in));
va
readPassword metodu kullanıcı tarafından veri okumak için yazılmış basit bir metottur. private char[] readPassword(InputStream in) throws IOException {
ww w. ja
char[] lineBuffer; char[] buf; int i;
buf = lineBuffer = new char[128]; int room = buf.length; int offset = 0; int c;
loop: while (true) { switch (c = in.read()) { case -1: case '\n': break loop;
case '\r': int c2 = in.read(); if ((c2 != '\n') && (c2 != -1)) { if (!(in instanceof PushbackInputStream)) { in = new PushbackInputStream(in); } ((PushbackInputStream)in).unread(c2); } else break loop;
20
}
i.c o
m
default: if (--room < 0) { buf = new char[offset + 128]; room = buf.length - offset - 1; System.arraycopy(lineBuffer, 0, buf, 0, offset); Arrays.fill(lineBuffer, ' '); lineBuffer = buf; } buf[offset++] = (char) c; break; }
if (offset == 0) { return null; }
}
}
return ret;
dil
char[] ret = new char[offset]; System.arraycopy(buf, 0, ret, 0, offset); Arrays.fill(buf, ' ');
va
2.2.4.5. SampleLoginModule ve SamplePrincipal sınıfları
ww w. ja
SampleLoginModule sınıfı LoginModule ara yüzünü gerçekleştirir. Oturum yapılandırma dosyasında belirtilir. Böylece uygulama kodu arka planda çalışan oturum modülünden bağımsızdır. Daha önce de ifade ettiğimiz gibi böylece uygulama kodunda herhangi bir değişiklik yapmadan arka planda kimlik denetimini gerçekleştiren oturum modülünü değiştirebiliriz. Tek yapmamız gereken yapılandırma dosyasında ilgili kayıttaki LoginModule nesnesini değiştirmektir. Önemli Not: SampleLoginModule sınıfı kullanıcı tarafından girilen kullanıcı ismi ve şifre üzerinden kimlik denetimi yapar. Bu şekilde kendi modülümüzü yazabileceğimiz gibi standart modüllerden bir veya birkaçını da kullanabiliriz. Bu noktada eğer uygulama kodu üzerinde çalışıyorsanız, bir LoginModule veya Principal gerçekleştiriminin nasıl yapıldığını bilmek zorunda değilsiniz. Bu nedenle eğitsel kapsamındaki bu iki gerçekleştirimi inceleme zorunluluğu yoktur. Tüm bilmeniz gereken uygulamanızı nasıl yazmanız ve yapılandırma işleminin nasıl yapılması gerektiğidir. 2.2.4.5.1. SampleLoginModule.java
package sample.module; import import import import import import
java.util.*; java.io.IOException; javax.security.auth.*; javax.security.auth.callback.*; javax.security.auth.login.*; javax.security.auth.spi.*;
21
i.c o
/** * Bu örnek modül kullanıcı ismi ve şifre üzerinden * kimlik denetimi yapar. * * Kullanıcı kimliği doğrulanırsa * kullanıcının ismi olan bir SamplePrincipal * nesnesi Subject nesnesine eklenir public class SampleLoginModule implements LoginModule {
m
import sample.principal.SamplePrincipal;
//ilk durum private Subject subject; private CallbackHandler callbackHandler; private Map sharedState; private Map options; private boolean debug = false;
// Kullanıcı ismi ve şifre private String username; private char[] password;
dil
// kimlik denetim durumu private boolean succeeded = false; private boolean commitSucceeded = false;
va
// Kullanıcıya ait temel nitelik private SamplePrincipal userPrincipal;
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
ww w. ja
this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options;
// initialize any configured options debug = "true".equalsIgnoreCase((String)options.get("debug"));
}
/** * Authenticate the user by prompting for a user name and password. * *
* * @return true in all cases since this LoginModule
* should not be ignored. * * @exception FailedLoginException if the authentication fails.
* * @exception LoginException if this LoginModule
* is unable to perform the authentication. */ public boolean login() throws LoginException { // prompt for a user name and password if (callbackHandler == null) throw new LoginException("Error: no CallbackHandler available " + "to garner authentication information from the user");
22
m
Callback[] callbacks = new Callback[2]; callbacks[0] = new NameCallback("user name: "); callbacks[1] = new PasswordCallback("password: ", false);
i.c o
try { callbackHandler.handle(callbacks); username = ((NameCallback)callbacks[0]).getName(); char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword(); if (tmpPassword == null) { // treat a NULL password as an empty password tmpPassword = new char[0]; } password = new char[tmpPassword.length]; System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length); ((PasswordCallback)callbacks[1]).clearPassword();
dil
} catch (java.io.IOException ioe) { throw new LoginException(ioe.toString()); } catch (UnsupportedCallbackException uce) { throw new LoginException("Error: " + uce.getCallback().toString() + " not available to garner authentication information " + "from the user"); }
va
// print debugging information if (debug) { System.out.println("\t\t[SampleLoginModule] " + "user entered user name: " + username); System.out.print("\t\t[SampleLoginModule] " + "user entered password: "); for (int i = 0; i < password.length; i++) System.out.print(password[i]); System.out.println(); }
ww w. ja
// verify the username/password boolean usernameCorrect = false; boolean passwordCorrect = false; if (username.equals("testUser")) usernameCorrect = true; if (usernameCorrect && password.length == 12 && password[0] == 't' && password[1] == 'e' && password[2] == 's' && password[3] == 't' && password[4] == 'P' && password[5] == 'a' && password[6] == 's' && password[7] == 's' && password[8] == 'w' && password[9] == 'o' && password[10] == 'r' && password[11] == 'd') {
// authentication succeeded!!! passwordCorrect = true; if (debug) System.out.println("\t\t[SampleLoginModule] " + "authentication succeeded"); succeeded = true;
23
}
// authentication failed -- clean out state if (debug) System.out.println("\t\t[SampleLoginModule] " + "authentication failed"); succeeded = false; username = null; for (int i = 0; i < password.length; i++) password[i] = ' '; password = null; if (!usernameCorrect) { throw new FailedLoginException("User Name Incorrect"); } else { throw new FailedLoginException("Password Incorrect"); }
i.c o
}
m
return true; } else {
ww w. ja
va
dil
/** *
This method is called if the LoginContext's * overall authentication succeeded * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules * succeeded). * *
If this LoginModule's own authentication attempt * succeeded (checked by retrieving the private state saved by the * login
method), then this method associates a * SamplePrincipal
* with the Subject
located in the * LoginModule
. If this LoginModule's own * authentication attempted failed, then this method removes * any state that was originally saved. * *
* * @exception LoginException if the commit fails. * * @return true if this LoginModule's own login and commit * attempts succeeded, or false otherwise. */ public boolean commit() throws LoginException { if (succeeded == false) { return false; } else { // add a Principal (authenticated identity) // to the Subject // assume the user we authenticated is the SamplePrincipal userPrincipal = new SamplePrincipal(username); if (!subject.getPrincipals().contains(userPrincipal)) subject.getPrincipals().add(userPrincipal); if (debug) { System.out.println("\t\t[SampleLoginModule] " + "added SamplePrincipal to Subject"); } // in any case, clean out state username = null; for (int i = 0; i < password.length; i++) password[i] = ' ';
24
}
}
m
password = null; commitSucceeded = true; return true;
ww w. ja
va
dil
i.c o
/** *
This method is called if the LoginContext's * overall authentication failed. * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules * did not succeed). * *
If this LoginModule's own authentication attempt * succeeded (checked by retrieving the private state saved by the * login
and commit
methods), * then this method cleans up any state that was originally saved. * *
* * @exception LoginException if the abort fails. * * @return false if this LoginModule's own login and/or commit attempts * failed, and true otherwise. */ public boolean abort() throws LoginException { if (succeeded == false) { return false; } else if (succeeded == true && commitSucceeded == false) { // login succeeded but overall authentication failed succeeded = false; username = null; if (password != null) { for (int i = 0; i < password.length; i++) password[i] = ' '; password = null; } userPrincipal = null; } else { // overall authentication succeeded and commit succeeded, // but someone else's commit failed logout(); } return true; } /** * Logout the user. * *
This method removes the SamplePrincipal
* that was added by the commit
method. * *
* * @exception LoginException if the logout fails. * * @return true in all cases since this LoginModule
* should not be ignored. */ public boolean logout() throws LoginException { subject.getPrincipals().remove(userPrincipal); succeeded = false;
25
m
i.c o
succeeded = commitSucceeded; username = null; if (password != null) { for (int i = 0; i < password.length; i++) password[i] = ' '; password = null; } userPrincipal = null; return true;
}
2.2.4.5.2. SamplePrincipal.java package sample.principal; import java.security.Principal;
va
dil
/** *
This class implements the Principal
interface * and represents a Sample user. * *
Principals such as this SamplePrincipal
* may be associated with a particular Subject
* to augment that Subject
with an additional * identity. Refer to the Subject
class for more information * on how to achieve this. Authorization decisions can then be based upon * the Principals associated with a Subject
. * * @version 1.4, 01/11/00 * @see java.security.Principal * @see javax.security.auth.Subject */ public class SamplePrincipal implements Principal, java.io.Serializable {
ww w. ja
/** * @serial */ private String name;
/** * Create a SamplePrincipal with a Sample username. * *
* * @param name the Sample username for this user. * * @exception NullPointerException if the name
* is null
. */ public SamplePrincipal(String name) { if (name == null) throw new NullPointerException("illegal null input"); }
this.name = name;
/** * Return the Sample username for this SamplePrincipal
. * *
26
m
* * @return the Sample username for this SamplePrincipal
*/ public String getName() { return name; }
i.c o
/** * Return a string representation of this SamplePrincipal
. * *
* * @return a string representation of this SamplePrincipal
. */ public String toString() { return("SamplePrincipal: " + name); }
va
dil
/** * Compares the specified Object with this SamplePrincipal
* for equality. Returns true if the given object is also a * SamplePrincipal
and the two SamplePrincipals * have the same username. * *
* * @param o Object to be compared for equality with this * SamplePrincipal
. * * @return true if the specified Object is equal equal to this * SamplePrincipal
. */ public boolean equals(Object o) { if (o == null) return false;
ww w. ja
if (this == o) return true;
if (!(o instanceof SamplePrincipal)) return false; SamplePrincipal that = (SamplePrincipal)o;
}
}
if (this.getName().equals(that.getName())) return true; return false;
/** * Return a hash code for this SamplePrincipal
. * *
* * @return a hash code for this SamplePrincipal
. */ public int hashCode() { return name.hashCode(); }
27
m
2.2.4.6. Oturum Yapılandırması
i.c o
Kimlik denetim teknolojilerinin uygulama kodunda herhangi bir değişiklik yapmadan kolaylıkla eklenebilir olduklarını daha önce ifade etmiştik. Böylelikle uygulamanın arka planda çalışan kimlik denetim teknolojilerinden bağımsız olması sağlanıyordu. Sistem yöneticisi kimlik denetim yapılarını, diğer ismiyle oturum modüllerini(LoginModule) belirler ve uygulamalar için hangi modüllerin kullanılacağını oturum yapılandırması(bir dosya veya veritabanı olabilir) içerisinde ifade eder. Yapılandırma bilgisinin kaynağı o anki javax.security.auth.login.Configuration gerçekleştirimine bağlıdır. Sun Microsystems’daki standart gerçekleştirim yapılandırma bilgisini bir dosyadan okur.
dil
Eğitsel kapsamında kullandığımız yapılandırma dosyası, sample jaas.config tek bir kayıt içerir. Sample { sample.module.SampleLoginModule required debug=true; };
va
SampleAcn isimli uygulamamız incelendiğinde LoginContext yaratılırken “Sample” ismiyle bu kayda referans verildiği görülmektedir. Böylece kimlik denetimi yapılırken yüklenen oturum modülü sample.module paketi içinde yer alan SampleLoginModule sınıfıdır. Kimlik denetiminin başarılı olması bu modülün denetimi sonucunu başarılı olarak döndürmesine bağlıdır. Oturum modülü sadece beklediği değerler ile girilen değerler uyuşuyorsa kimliği doğrular.
ww w. ja
Debug seçeneğinin true olarak belirlenmesiyle denetim süresince ek olarak kullanıcıya geribildirim yapılır. 2.2.4.7. Örnek Kodun Çalıştırılması
1. sample jaas.config dosyasını bir klasöre yerleştirin. 2. Bu klasörün altında sample isminde bir alt klasör yaratıp içine SampleAcn ve MyCallbackHandler sınıflarını içeren SampleAcn.java dosyasını kaydedin. 3. sample klasörü altında da module klasörü yaratın ve SampleLoginModule.java dosyasını buraya kaydedin. 4. sample klasörü altında ek olarak bir principal klasörü yaratın ve SamplePrincipal dosyasını buraya kaydedin. 5. En üstteki klasördeyken SampleAcn.java, SampleLoginModule.java ve SamplePrincipal.java dosyalarını derleyin.
28
m
javac sample/SampleAcn.java sample/module/SampleLoginModule.java sample/principal/SamplePrincipal.java
6. SampleAcn uygulamasını aşağıdaki satır ile çalıştırın.
i.c o
-Djava.security.auth.login.config==sample_jaas.config sample.SampleAcn
Uygulama sizden kullanıcı ismi ve şifre için istemde bulunacaktır. SampleLoginModule de girilen verilerin kontrolünü yapar ve sonucu döndürür. Debug seçeneğinin bir sonucu olarak kimlik denetim sürecinde çeşitli mesajlarla karşılaşacaksınız. Kimliğiniz doğrulanırsa aşağıdaki mesajı alırsınız. Authentication succeeded!
Authentication failed:
dil
Kimlik denetim başarısız olursa aşağıdaki iletiyi alırsınız.
Ek olarak örneğin şifreyi hatalı girdiyseniz;
va
Authentication failed: Password Incorrect
Şeklinde bilgilendirilirsiniz.
ww w. ja
2.3. JAAS’ın Yetkilendirme(Authorization) Amaçlı Kullanımı
JAAS ile yetkilendirme işleminde erişim yetkilerini dağıtırken sadece kodun kaynağını ve konumunu temel almayız; bunun dışında kodu kimin çalıştırdığı bilgisini de ele alırız. Kullanıcı yetkilendirmesi yapılabilmesi için;
2.2. de gerçekleştirimi anlatıldığı gibi kimlik denetiminden geçmiş olması gerekir. Temel nitelik tabanlı(Principal-based) kayıtlar güvenlik politikasında yapılandırılmış olmalıdır. Kimlik denetiminden geçen kullanıcıyı gösteren Subject nesnesi o anki erişim kontrol bağlamıyla (Access control context) ilişkilendirilmiş olmalıdır.
Policy soyut sınıfı ve yetkilendirmeye özel AuthPermission PrivateCredentialPermission sınıfları aşağıda anlatılmıştır.
29
ve
m
2.3.1. Policy
erişim kontrol politikasını simgeleyen soyut bir sınıftır. J2SDK 1.4’te güncellenerek temel nitelik tabanlı sorguları da destekler hale gelmiştir. java.security.Policy
i.c o
2.3.2. AuthPermission
JAAS için gerekli izinlerin verilmesini sağlar. İzni verilmek istenen işlevin ismini içerir. Policy, Subject, LoginContext ve Configuration nesnelerine izinsiz erişimi engellemek için kullanılır.
2.3.3. PrivateCredentialPermission
2.3.4. Örnek Uygulama
dil
İsminden de anlaşılacağı üzere gizli credential kümelerine izinsiz erişimi engellemek üzere kullanılır.
va
Yukarıda sıraladığımız maddeler doğrultusunda yetkilendirme işlevinin nasıl gerçekleştirildiğini kod örnekleri üzerinden görelim. Kullanıcının kimlik denetiminden nasıl geçtiğini kimlik denetimi kısmında görmüştük. Sırada temel nitelik tabanlı(Principal-based) kayıtların nasıl yapılandırıldıkları sorusuna cevap aramak gerekir.
ww w. ja
2.3.4.1. Politika Dosyasına Temel Nitelik Tabanlı Deyimlerin Yazılması Politika dosyasındaki grant deyimleri seçeneğe bağlı olarak bir ya da birçok temel nitelik(Principal) alanı içerebilir. Temel nitelikler aracılığıyla o niteliğe sahip kullanıcının erişim yetkileri düzenlenmiş olur. Bu nedenle grant deyiminin yapısı şu şekildedir:
grant , { permission izni belirten sınıf "hedef isim", "işlev"; .... permission izni belirten sınıf "hedef isim", "işlev"; };
Köşeli ayraç içindeki kısımlar seçeneğe bağlı kısımlardır. Bir temel nitelik alanı aşağıdaki gibidir: Principal Principal_sınıfı "principal_ismi"
30
i.c o
m
Principal sınıfı daha önce de söylediğimiz gibi Principal ara yüzünü gerçekleştirmiş olmalıdır. Her bir Principal nesnesinin ilişkilendirilmiş bir ismi vardır ve bu isme getName metodu ile erişilebilir. Eğitsel kapsamında kimlik denetiminde de kullandığımız SamplePrincipal sınıfını kullanacağız; yani Principal_sınıfı kısmına bu nesneyi yazacağız. Kimlik denetiminden geçebilecek tek kullanıcı isminin SampleLoginModule sınıfını incelediğimizde “testUser” olduğunu görürüz. Bu nedenle Principal_ismi kısmına da “testUser” yazarız. Bir grant deyiminde örneğimizden farkı olarak birden fazla temel nitelik alanı yazmak da mümkündür. Eğitsel kapsamındaki kodun bağlı olduğu politika dosyasında aşağıdaki deyim yer almaktadır: grant codebase "file:./SampleAction.jar", Principal sample.principal.SamplePrincipal "testUser" {
dil
};
permission java.util.PropertyPermission "java.home", "read"; permission java.util.PropertyPermission "user.home", "read"; permission java.io.FilePermission "foo.txt", "read";
va
Belirtilen izinler, SampleAction.jar paketi içinden kodu çalıştıran ve belirtilen temel niteliği içeren özneye verilmiştir. 2.3.4.2. Bir Subject Nesnesinin Erişim Kontrol Bağlamıyla İlişkilendirilmesi
ww w. ja
Bunun için Subject sınıfının statik metodu olan doAs çağırılır. Kimliği doğrulanmış Subject nesnesi ve gerçekleştirilmek istenen işlev parametre olarak geçilir. Metot Subject nesnesini o anki erişim kontrol bağlamıyla(Access Control Context) ile ilişkilendirir ve işlevin run metodunu çağırır. Run metodu özne tarafından gerçekleştirilecek işlevin tüm detaylarını içerir.
Eğitsel kapsamında doAs metodu yerine, aynı işlevi gören ancak ek olarak AccessControlContext nesnesini parametre olarak alan doAsPriveleged metodunu kullanacağız. Bu metot doAs’den farklı olarak özneyi o anki erişim kontrol bağlamıyla ilişkilendirmek yerine parametre olarak geçilen AccessControlContext ile ilişkilendirir. Parametre null değerini de taşıyabilir.
SampleAzn.java: Kimlik denetim kısmında incelediğimiz SampleAcn.java
dosyasına benzerdir. Ek olarak Subject.doAsPriveleged metodunu çağıran kodu içermektedir. SampleAction.java: SampleAction sınıfını içerir. PrivelegedAction ara yüzünü gerçekleştirmiştir ve tüm işlevi gerçekleştiren run metodu bulunur. SampleLoginModule.java ve SamplePrincipal dosyaları kimlik denetimi kısmında incelediğimiz dosyalarla birebir aynıdır.
31
m
2.3.4.3. SampleAzn.java
i.c o
Kimlik denetiminde yazdığımız SampleAcn uygulamasının gerçekleştirdiği gibi bir LoginContext nesnesi yaratır ve kimlik denetimi için login metodunu çağırır. Kimlik denetimi başarılı olursa getSubject metodu ile ilişkili Subject nesnesine erişilir. Subject mySubject = lc.getSubject();
Subject nesnesini öznenin sahip olduğu bazı temel niteliklerle ilişkilendirmek için bu niteliklere karşılık gelen Principal nesneleri Subject nesnesine kaydedilir. Bu işlemin ardından doAsPriveleged metodu çağırılır. Parametre olarak da kimlik denetiminden geçmiş özneye karşılık gelen mySubject(Subject) olgumuz, işleve karşılık gelen action(SampleAction) ve AccessControlContext olarak da null geçilir.
olgusu aşağıdaki gibi yaratılır:
dil
SampleAction
PrivilegedAction action = new SampleAction(); doAsPriveleged
metodu da aşağıdaki gibi çağırılır.
va
Subject.doAsPrivileged(mySubject, action, null);
doAsPriveleged metodu SampleAction sınıfının run metodunu çağırır. Bu metot da işlevi belirtilen özne(mySubject) adına gerçekleştirmiş olur. AccessControlContext alanına null değeri geçerek öznenin boş bir erişim kontrol bağlamıyla ilişkilendirileceği ifade edilir.
ww w. ja
2.3.4.4. SampleAction.java
SampleAction sınıfını içerir. Bu sınıf java.security.PrivelegedAction ara yüzünü gerçekleştirir ve mySubject olarak işlevi gerçekleştirmemizi sağlayan run metodunu içerir. Eğitsel kapsamında izinsiz gerçekleştirimi mümkün olmayan üç ayrı işlemi gerçekleştireceğiz.
java.home sistem niteliğinin değerini yaz ve oku. user.home sistem niteliğinin değerini yaz ve oku. Bulunulan klasörde x.txt dosyasının var olup olmadığını belirle.
package sample;
import java.io.File; import java.security.PrivilegedAction; public class SampleAction implements PrivilegedAction { public Object run() {
32
m
System.out.println("\nYour java.home property value is: " + System.getProperty("java.home")); System.out.println("\nYour user.home property value is: " + System.getProperty("user.home"));
i.c o
File f = new File("x.txt"); System.out.print("\nx.txt does "); if (!f.exists()) System.out.print("not ");
System.out.println("exist in the current working directory."); }
return null;
dil
}
2.3.4.5. Oturum Yapılandırma Dosyası
va
Kimlik denetim kısmında incelediğimiz dosya ile birebir aynıdır. Sample { sample.module.SampleLoginModule required debug=true; };
ww w. ja
LoginContext yaratılırken kayıt ismi olarak “Sample” parametre geçilir. Böylece oturum modülü olarak sample.module paketi içindeki SampleLoginModule sınıfı kullanılır; kimlik denetimi bu sınıfın olgusu üzerinden gerçekleştirilir. Denetimin başarılı olması bu modülün denetim sonucunu başarılı olarak döndürmesine bağlıdır. Oturum modülü sadece beklediği değerler ile girilen değerler uyuşuyorsa kimliği doğrular.
Debug seçeneğinin true olarak belirlenmesiyle denetim süresince ek olarak kullanıcıya geribildirim yapılır.(Oturum başarılı veya başarısız gibi) 2.3.4.6. Politika Dosyası
SampleAzn ve SampleAction sınıfları gizlilik açısından önem arz eden bazı işlemleri gerçekleştirmektedirler. Bu işlemlerin gerçekleştirilebilmesi için politika dosyası üzerinden bu kritik işlemleri gerçekleştirme yetkisi ilgili kullanıcıya verilmelidir. SampleAzn sınıfının gerek duyduğu yetkiler
LoginContext yaratır. Subject sınıfının doAsPriveleged statik metodunu çağırır.
33
i.c o
m
LoginContext yaratma işlemi kimlik denetiminde gerçekleştirdiğimiz gibidir. Bu nedenle "createLoginContext.Sample" hedef ismiyle javax.security.auth.AuthPermission üzerinden izin verilmelidir. Aynı şekilde doAsPriveleged metodunu çağırabilmek için "doAsPrivileged" hedef ismi ile yetki verilmelidir. SampleAzn.java dosyasının SampleAzn.jar içinde yer aldığını varsayarak SampleAzn kaynak koduna izinler aşağıdaki politika dosyası içerisindeki grant deyimiyle aşağıdaki biçimde verilir.
dil
grant codebase "file:./SampleAzn.jar" { permission javax.security.auth.AuthPermission "createLoginContext.Sample"; permission javax.security.auth.AuthPermission "doAsPrivileged"; };
SampleAction sınıfının gerek duyduğu yetkiler
java.home sistem niteliğinin değerini okur. user.home sistem niteliğinin değerini okur. Bulunulan klasörde x.txt dosyasının var olup olmadığını kontrol eder.
va
Bu işlemleri gerçekleştirmek için gerekli izinler aşağıdaki biçimde verilir.
ww w. ja
permission java.util.PropertyPermission "java.home", "read"; permission java.util.PropertyPermission "user.home", "read"; permission java.io.FilePermission "x.txt", "read";
Bu izinleri SampleAction.jar dosyası içine yerleştireceğimiz SampleAction.class sınıfına veririz. Sadece kodun konumuna bağlı denetim yapmadığımız için ek olarak hangi kullanıcının bu yetkiye sahip olduğunu da belirtmemiz gerekir. Kullanıcıya yetkinin nasıl verildiğini, politika dosyasına temel nitelik tabanlı(Principal-based) deyimlerin yazılmasında incelemiştik. grant codebase "file:./SampleAction.jar", Principal sample.principal.SamplePrincipal "testUser" {
};
permission java.util.PropertyPermission "java.home", "read"; permission java.util.PropertyPermission "user.home", "read"; permission java.io.FilePermission "x.txt", "read";
Kod SampleAction.jar konumundan ve “testUser” niteliğine sahip kullanıcı tarafından çalıştırılıyorsa, yukarıdaki işlemleri gerçekleştirme yetkisine sahip olur. Böylece JAAS’ın asıl önem taşıyan fonksiyonunu örneklemiş oluyoruz.
34
m
SampleLoginModule sınıfının gerek duyduğu yetkiler
i.c o
SampleLoginModule sınıfını incelediğimizde yetki verilmesi gereken tek işlemin “modifyPrincipals” olduğunu görürüz. Kimlik denetimi başarıya ulaşırsa ilgili nitelik ile özneyi ilişkilendirir. Bunu yaparken de SamplePrincipal nesnesini yarattığımız Subject nesnesine kaydeder. Bunu yapabilmek için de gerekli yetkinin verilmiş olması gerekir. grant codebase "file:./SampleLM.jar" { permission javax.security.auth.AuthPermission "modifyPrincipals"; };
dil
2.3.4.7. Politika Dosyasının Son Görünümü(sampleazn.policy)
/** Örnek Uygulamamız için Java 2 Erişim Kontrol Politikası **/ /* Örnek oturum modülüne gerekli yetkileri ver */ grant codebase "file:./SampleLM.jar" { permission javax.security.auth.AuthPermission "modifyPrincipals"; };
va
grant codebase "file:./SampleAzn.jar" {
permission javax.security.auth.AuthPermission "createLoginContext.Sample"; permission javax.security.auth.AuthPermission "doAsPrivileged"; };
ww w. ja
/** SampleAzn tarafından yaratılan SampleAction nesnesi üzerinde ** kullanıcı tabanlı verilen yetkiler ** Böylece “testUser” niteliğini taşıyan kullanıcı ** belirtilen yetkilere sahip olur. **/ grant
};
codebase "file:./SampleAction.jar", Principal sample.principal.SamplePrincipal "testUser" {
permission java.util.PropertyPermission "java.home", "read"; permission java.util.PropertyPermission "user.home", "read"; permission java.io.FilePermission "foo.txt", "read";
35
m
2.3.4.8. Kodun Çalıştırılması 1. sample jaas.config ve sampleazn.policy dosyasını bir klasöre yerleştirin.
i.c o
2. sample isminde bir alt klasör yaratın. SampleAzn.java ve SampleAction.java dosyalarını bu alt klasöre dahil edin. 3. sample klasörü altında bir module alt klasörü oluşturun ve SampleLoginModule.java dosyasını bu alt klasöre dahil edin.
4. sample klasörü altında bu sefer principal adında bir klasör oluşturun ve SamplePrincipal.java dosyasını bu klasöre dahil edin. 5. En üstteki klasördeyken tüm dosyaları derleyin.
dil
javac sample/SampleAction.java sample/SampleAzn.java sample module/SampleLoginModule.java sample / principal /SamplePrincipal.java
va
6. SampleAzn.class ve MyCallbackHandler.class dosyalarını içeren bir SampleAzn.jar dosyası oluşturun. Bu iki sınıfın SampleAzn.java dosyasında tanımlandığını unutmayın. jar -cvf SampleAzn.jar sample/SampleAzn.class sample/MyCallbackHandler.class
7. SampleAction sınıfını içeren bir SampleAction.jar dosyası yaratın.
ww w. ja
jar -cvf SampleAction.jar sample/SampleAction.class
8. SampleLoginModule ve SamplePrincipal dosyasını içeren bir jar dosyası yaratın. jar -cvf SampleLM.jar sample/module/SampleLoginModule.class sample/principal/SamplePrincipal.class
9. SampleAzn uygulamasını çalıştırın.
java -classpath SampleAzn.jar;SampleAction.jar;SampleLM.jar -Djava.security.manager -Djava.security.policy==sampleazn.policy -Djava.security.auth.login.config==sample_jaas.config sample.SampleAzn
Uygulama kullanıcı ismi ve şifre girmeniz için istemde bulunacaktır. Ardından SampleLoginModule tarafından kimlik denetiminden geçeceksiniz. Oturum başarıyla açılırsa SampleAction işlevi sizin tarafınızdan çalıştırılacak; ancak işlev içerisindeki
36
m
işlemleri gerçekleştirebilmeniz için ilgili yetkilerin sampleazn.policy dosyası üzerinden şahsınıza verilmiş olması gerekmektedir.
i.c o
3. Sonuç
Eğitsel kapsamında JAAS’ın gerçekleştirim detaylarını örnek kodlar üzerinden anlattık. Özetlemek gerekirse JAAS, Java 2 Güvenlik Mimarisine kolaylıkla eklenebilme özelliğine sahip, kod tabanlı denetime ek olarak kullanıcı tabanlı denetim desteğini de sunarak Java’nın güvenlik mekanizmasını güçlendiren bir programlama ara yüzü olarak geliştirilmiştir. JAAS’ın ilk örnek gerçekleştirimi standart Java paketine dâhil edilmiş, yaklaşık yirmi beş sınıf 4 ayrı paket içinde bölümlenmiştir.
ww w. ja
va
dil
Java’nın gün geçtikçe masaüstü uygulamaların dışında dağıtılmış sistemlerde de kullanım alanını genişletiyor olması beraberinde güvenlik gereksinimlerini de artırmaktadır. Örneğin ağ üzerinden gelebilecek tehlikelere karşı uzaktan yordam çağırma(RMI) işlemini güvenli hale getirmek çok önemlidir. Bunun gibi güvenliği büyük önem arz eden birçok üst düzey sistem bulunmaktadır. Başka bir örnek ise birçok kullanıcının etkileşim halinde olduğu elektronik ticaret sistemlerinin güvenliğidir. Bunların dışında Kerberos ve IPv6 ağ güvenlik protokolleri gibi alt seviye güvenlik protokolleri bulunmaktadır. Tüm bu güvenlik problemlerinin çözümü açısından JAAS büyük önem taşır.
37