LDAP/SASL/GSSAPI/Kerberos编程API(2)--krb5客户端

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]用户主体.

二.客户机安装开发库
[email protected]:/# 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.编译
[email protected]:~$ 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
[email protected]:~$ cat /etc/resolv.conf
nameserver 10.0.3.102
[email protected]:~$

下面测试失败和成功两种情况

1)当都没有/etc/krb5.conf和服务资源记录时
[email protected]:~$ ./krbonlylogin
begin get init creds password
Err: Failed to get init creds password -> Cannot find KDC for realm "CTP.NET"
[email protected]:~$
认证失败,找不KDC

2)当只有单独/etc/krb5.conf或单独服务资源记录时
为方便测试,客户端输入口令是写死在源码里.为测试正确/错误密码两种情况,可到Kerberos服务器设置[email protected]用户主体密码

错误的密码
[email protected]:~$ ./krbonlylogin
begin get init creds password
Err: Failed to get init creds password -> Preauthentication failed
[email protected]:~$
认证失败

正确的密码
[email protected]:~$ ./krbonlylogin
begin get init creds password
get init creds password OK
[email protected]:~$
认证成功

3)先测试单独服务资源记录配置成功认证,然后创建/etc/krb5.conf,其KDC地址乱填,即这时服务资源记录和/etc/krb5.conf同时存在

[email protected]:~$ ./krbonlylogin
begin get init creds password
Err: Failed to get init creds password -> Cannot contact any KDC for realm ‘CTP.NET‘
[email protected]:~$
提示找不到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.编译
[email protected]:~$ gcc -o krbteststore krbteststore.c -lkrb5

4.运行

[email protected]:~$ ./krbteststore
begin get init creds password
get init creds password OK
Successfully store creds
[email protected]:~$ ls /tmp
krb5cc_1000
[email protected]:~$

可见到krbteststore程序生成了票据krb5cc_1000文件

我已事先安装好一台LDAP服务器(10.0.3.11),并配置LDAP可使用GSSAPI认证.测试环境的krb5客户机已安装好LDAP客户端,下面是测试LDAP客户程序读取上面生成的票据通过GSSAPI是否正常
[email protected]:~$ 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用户条目
[email protected]:~$
说明生成的票据是正常的,认证成功

原文地址:https://blog.51cto.com/13752418/2479556

时间: 2024-08-30 17:34:47

LDAP/SASL/GSSAPI/Kerberos编程API(2)--krb5客户端的相关文章

Kerberos+LDAP+NFSv4 实现单点登录(续4)--SASL/GSSAPI

前篇<Kerberos+LDAP+NFSv4 实现单点登录(续1)--dns+dhcp>的krb5 + ldap + bind9 + bind9-dyndb-ldap 全面升级到debian 10,出现bind9-dyndb-ldap的GSSAPI+krb5_keytab认证机制无法连接ldap数据库.查看日志:SASL/GSSAPI authentication startedError: Local errorAdditional info: SASL(-1): generic failu

HBase编程 API入门系列之delete(管理端而言)(9)

大家,若是看过我前期的这篇博客的话,则 HBase编程 API入门之delete(客户端而言) 就知道,在这篇博文里,我是在客户端里删除HBase表的. 这里,我带领大家,学习更高级的,因为,在开发中,尽量不能客户端上删除表. 所以,在管理端来删除HBase表.采用线程池的方式(也是生产开发里首推的) package zhouls.bigdata.HbaseProject.Pool; import java.io.IOException;import java.util.concurrent.E

HBase编程 API入门之create(管理端而言)

大家,若是看过我前期的这篇博客的话,则 HBase编程 API入门之put(客户端而言) 就知道,在这篇博文里,我是在HBase Shell里创建HBase表的. 这里,我带领大家,学习更高级的,因为,在开发中,尽量不能去服务器上创建表. 所以,在管理端来创建HBase表.采用线程池的方式(也是生产开发里首推的) package zhouls.bigdata.HbaseProject.Pool; import java.io.IOException;import java.util.concur

HBase编程 API入门之delete

前面的基础,是 HBase编程 API入门之put HBase编程 API入门之get hbase(main):001:0> scan 'test_table2'ROW COLUMN+CELL row_04 column=f1:name, timestamp=1478117286377, value=Andy3 row_04 column=f2:name, timestamp=1478117286377, value=Andy3 1 row(s) in 1.5670 seconds hbase(

HBase编程 API入门之put

[[email protected] conf]$ cat regionservers HadoopMasterHadoopSlave1HadoopSlave2 <configuration> <property> <name>hbase.zookeeper.quorum</name> <value>HadoopMaster,HadoopSlave1,HadoopSlave2</value> </property> <

HBase编程 API入门之get

前面是基础. HBase编程 API入门之put package zhouls.bigdata.HbaseProject.Test1; import javax.xml.transform.Result; import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.hbase.HBaseConfiguration;import org.apache.hadoop.hbase.TableName;import org.a

Hadoop MapReduce编程 API入门系列之压缩和计数器(三十)

不多说,直接上代码. Hadoop MapReduce编程 API入门系列之小文件合并(二十九) 生成的结果,作为输入源. 代码 package zhouls.bigdata.myMapReduce.ParseTVDataCompressAndCounter; import java.net.URI; import java.util.List;import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.conf.Co

Linux程序设计学习笔记----网络通信编程API及其示例应用

转载请注明出处, http://blog.csdn.net/suool/article/details/38702855. BSD Socket 网络通信编程 BSD TCP 通信编程流程 图为面向连接的Socket通信的双方执行函数流程.使用TCP协议的通信双方实现数据通信的基本流程如下 建立连接的步骤 1.首先服务器端需要以下工作: (1)调用socket()函数,建立Socket对象,指定通信协议. (2)调用bind()函数,将创建的Socket对象与当前主机的某一个IP地址和TCP端口

Android 网络编程 API笔记 - java.net 包 权限 地址 套接字 相关类 简介

Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- Android包 : android.net 包 (1接口, 19类, 3枚举, 1异常), android.net.http 包 (6类), android.net.nsd 包 (3接口, 2类), android.net.rtp (4类), android.net.sip 包 (1接口, 9类, 1