krb5 API有兩個可用的庫:MIT和Heimdal,兩個庫的API不一樣,一方客戶端的API連接上另一方服務端基本上是沒問題的.
API中的kadmin兩個庫則是完全不兼容,可從MIT和Heimdal兩個的kadmin應用工具看出,連接對方的kadmin服務端是不成功的.
kadmin目的是爲遠程操控Kerberos服務器,一般我們開發Kerberos應用很少以此爲目標,都是直接使用它們各自的kadmin應用工具,所以kadmin不兼容也沒多大問題.
我們的目標是Kerberos認證功能,所以使用MIT或是Heimdal都沒問題.
MIT是主流,本文以此爲例
一.實驗環境
平臺 : debian 11
我已事先安裝好一臺Kerberos服務器(KDC),領域爲CTP.NET,並創建了[email protected]用戶主體.
二.客戶機安裝開發庫
root@debian:/# apt-get install libkrb5-dev
三.最簡單krb5認證--不生成票據
1.源代碼
//源文件名:krbonlylogin.c
#include <stdio.h>
#include <krb5.h>
int main(void)
{
krb5_context context = NULL;
krb5_error_code krberr;
krb5_principal kprincpw = NULL;
krb5_creds * my_creds_ptr = NULL;
krb5_creds my_creds;
const char * errmsg;
krberr = krb5_init_context(&context);
if (krberr) {
errmsg = krb5_get_error_message(NULL, krberr);
printf("Err: Kerberos context initialization failed -> %s\n", errmsg);
goto cleanup;
}
krberr = krb5_parse_name(context, "[email protected]", &kprincpw); //用戶主體
if (krberr) {
errmsg = krb5_get_error_message(context, krberr);
printf("Err: Failed to parse princpal %s -> %s\n", errmsg);
goto cleanup;
}
const char *password="linlin"; //口令
printf("begin get init creds password\n");
krberr = krb5_get_init_creds_password(context, &my_creds,kprincpw, (char *)password,NULL,NULL,0,NULL,NULL);//也在此讀取/etc/krb5.conf或服務資源記錄得到KDC
if (krberr) { //認證失敗
errmsg = krb5_get_error_message(context, krberr);
printf("Err: Failed to get init creds password -> %s\n", errmsg);
goto cleanup;//退出
}
//認證成功
my_creds_ptr = &my_creds;
printf("get init creds password OK\n");
/*
認證成功就繼續處理事情,如:
假設本程序是login(登錄主機)程序,則執行execv 運行shell
本例省略
*/
cleanup:
if (kprincpw) krb5_free_principal(context, kprincpw);
if (my_creds_ptr) krb5_free_cred_contents(context, &my_creds);
if (context) krb5_free_context(context);
return 0;
}
2.解析
本程序僅僅測試是否通過Kerberos認證,沒處理什麼事情
典型的應用如unix系統本地登錄程序PAM插件libpam-krb5
3.編譯
linlin@debian:~$ gcc -o krbonlylogin krbonlylogin.c -lkrb5
4.運行
上面的客戶端源碼沒顯式指定連接Kerberos服務器地址,本文的目的是用最簡練的方式來表達如何使用API,並且本人也沒深入探究能否/如何在程序裏指定各參數(如服務器地址).
有兩個方式可配置連接到Kerberos:
/etc/krb5.conf
SRV(服務)資源記錄
這兩個方式的配置不再介紹,請參考<Kerberos+LDAP+NFSv4 實現單點登錄(續1)--dns+dhcp>(https://blog.51cto.com/13752418/2395345)
使用服務資源記錄,要用到了DNS,所以客戶端機器還需配置/etc/resolv.conf文件,假設DNS服務器地址是10.0.3.102
linlin@debian:~$ cat /etc/resolv.conf
nameserver 10.0.3.102
linlin@debian:~$
下面測試失敗和成功兩種情況
1)當都沒有/etc/krb5.conf和服務資源記錄時
linlin@debian:~$ ./krbonlylogin
begin get init creds password
Err: Failed to get init creds password -> Cannot find KDC for realm "CTP.NET"
linlin@debian:~$
認證失敗,找不KDC
2)當只有單獨/etc/krb5.conf或單獨服務資源記錄時
爲方便測試,客戶端輸入口令是寫死在源碼裏.爲測試正確/錯誤密碼兩種情況,可到Kerberos服務器設置[email protected]用戶主體密碼
錯誤的密碼
linlin@debian:~$ ./krbonlylogin
begin get init creds password
Err: Failed to get init creds password -> Preauthentication failed
linlin@debian:~$
認證失敗
正確的密碼
linlin@debian:~$ ./krbonlylogin
begin get init creds password
get init creds password OK
linlin@debian:~$
認證成功
3)先測試單獨服務資源記錄配置成功認證,然後創建/etc/krb5.conf,其KDC地址亂填,即這時服務資源記錄和/etc/krb5.conf同時存在
linlin@debian:~$ ./krbonlylogin
begin get init creds password
Err: Failed to get init creds password -> Cannot contact any KDC for realm 'CTP.NET'
linlin@debian:~$
提示找不到KDC服務器,說明是以/etc/krb5.conf配置爲優先,裏邊的不正確KDC地址導致失敗後也不會去嘗試服務資源記錄
4)小結
客戶機可以不需krb5.conf文件,在網絡有搭建DNS的情況下,可通過服務資源記錄獲得KDC地址.當這兩個同時存在時,以/etc/krb5.conf爲優先(不管其成功還是失敗),即使服務資源記錄配置正確.
三.krb5認證-存儲票據
基於上面代碼增加生成票據
1.源代碼
//源文件名:krbteststore.c
#include <stdio.h>
#include <krb5.h>
int main(void)
{
krb5_context context = NULL;
krb5_error_code krberr;
krb5_principal kprincpw = NULL;
krb5_creds * my_creds_ptr = NULL;
krb5_creds my_creds;
const char * errmsg;
krberr = krb5_init_context(&context);
if (krberr) {
errmsg = krb5_get_error_message(NULL, krberr);
printf("Err: Kerberos context initialization failed -> %s\n", errmsg);
goto cleanup;
}
krberr = krb5_parse_name(context, "[email protected]", &kprincpw);
if (krberr) {
errmsg = krb5_get_error_message(context, krberr);
printf("Err: Failed to parse princpal %s -> %s\n", errmsg);
goto cleanup;
}
const char *password="linlin";
printf("begin get init creds password\n");
krberr = krb5_get_init_creds_password(context, &my_creds,kprincpw, (char *)password,NULL,NULL,0,NULL,NULL);
if (krberr) {
errmsg = krb5_get_error_message(context, krberr);
printf("Err: Failed to get init creds password -> %s\n", errmsg);
goto cleanup;
}
my_creds_ptr = &my_creds;
printf("get init creds password OK\n");
//--v-- 增加生成票據
krb5_ccache ccache = NULL;
/*
//生成票據到本進程內存裏,本程序沒做驗證票據的事情,所以不實驗內存票據
//krberr = krb5_cc_resolve(context, "MEMORY:dhcp_ld_krb5_cc", &ccache);
*/
//生成票據到臨時目錄裏,由ldapwhoami驗證是否有效 ,實驗的登錄用戶的uid是1000,所以指定票據文件名krb5cc_1000
krberr = krb5_cc_resolve(context, "FILE:/tmp/krb5cc_1000", &ccache);
if (krberr) {
errmsg = krb5_get_error_message(context, krberr);
printf("Err: Couldnt resolve ccache -> %s\n", errmsg);
goto cleanup;
}
krberr = krb5_cc_initialize(context, ccache, kprincpw);
if (krberr) {
errmsg = krb5_get_error_message(context, krberr);
printf("Err: Failed to init ccache -> %s\n", errmsg);
goto cleanup;
}
krberr = krb5_cc_store_cred(context, ccache, &my_creds);
if (krberr) {
errmsg = krb5_get_error_message(context, krberr);
printf("Err: Failed to store credentials -> %s\n", errmsg);
goto cleanup;
}
printf("Successfully store creds\n");
//--^--
cleanup:
if (ccache) krb5_cc_close(context, ccache);//這裏雖close,但不會銷燬票據"FILE:/tmp/krb5cc_1000",見下.但就不知是否會銷燬內存票據
if (kprincpw) krb5_free_principal(context, kprincpw);
if (my_creds_ptr) krb5_free_cred_contents(context, &my_creds);
if (context) krb5_free_context(context);
return 0;
}
2.解析
上面代碼中的函數全是MIT krb5開發庫API函數.源碼篇幅不長,很容易看懂,不再解析.
3.編譯
linlin@debian:~$ gcc -o krbteststore krbteststore.c -lkrb5
4.運行
linlin@debian:~$ ./krbteststore
begin get init creds password
get init creds password OK
Successfully store creds
linlin@debian:~$ ls /tmp
krb5cc_1000
linlin@debian:~$
可見到krbteststore程序生成了票據krb5cc_1000文件
我已事先安裝好一臺LDAP服務器(10.0.3.11),並配置LDAP可使用GSSAPI認證.測試環境的krb5客戶機已安裝好LDAP客戶端,下面是測試LDAP客戶程序讀取上面生成的票據通過GSSAPI是否正常
linlin@debian:~$ ldapwhoami -Y GSSAPI -h 10.0.3.11
SASL/GSSAPI authentication started 已能認krbteststore生成的票據
SASL username: [email protected] 可見到用戶主體
SASL SSF: 56
SASL data security layer installed.
dn:uid=krblinlin,cn=gssapi,cn=auth 已得到LDAP用戶條目
linlin@debian:~$
說明生成的票據是正常的,認證成功