Linux上C++通过LDAP协议使用kerberos认证AES加密连接到AD服务器

news2024/11/16 15:47:46

一.前言

        记录自己在实现这个流程遇到的各种问题,因为我也是看了许多优质的文章以及组内大佬的帮助下才弄成的,这里推荐一个大佬的文章,写的非常优秀,比我这篇文章写得好得很多,最后我也是看这个大佬的代码最终才实现的,特别鸣谢。看这篇文章前提你得足够的了解AD,kerberos,ldap等,CSDN这类文章就很多,我就不献丑了。

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

LDAP/SASL/GSSAPI/Kerberos编程API(3)--LDAP/SASL

不止这两篇,大佬很详细可自行查看

 二,环境支持

        AD服务器:Windows server 2022 中文版

        Linux:ubuntu 20

        openldap:sudo apt-get install libldap-dev    //代码所需要的ldap.h

        GSSAPI支持:sudo apt-get install libsasl2-modules-gssapi-mit   //使LDAP支持GSSAPI认证,kerberos属于GSSAPI认证方式之一,否则运行时会出现下面报错。

        

Error: Unknown authentication method
Additional info: SASL(-4): no mechanism available: No worthy mechs found

        krb5环境支持:sudo apt-get install krb5-user     //krb5API的调用,因为kerberos认证过程中产生的TGT票据是我们手动调用API实现的 ,而并非使用kinit生成。

三,API详解

      1.生成TGT票据

            因为我写的是公司项目代码,不可以拿出来,我就直接引用前言大佬的代码了,冒犯还请联系我删除。各个API的功能和参数作用我都写在代码块中了,注释是我加的,代码是大佬的。

大致流程:1.初始化安全的上下文->2.使用用户名,密码换区TGT->3.通过TGT换区票据

#include <ldap.h>
#include <stdio.h>
#include <stdlib.h>
#include <krb5.h>

//krb5_get_init_creds_password函数所使用的回调函数,这个函数必须返回成功值 LDAP_SUCCESS,否则基本都失败,因为我通过循环试了1000值。不需要了解他的功能,我猜测的是这个函数能够在我们进行最后的bind之前进行其他的判断,如果失败不返回LDAP_SUCCESS,作为最后一道屏障
static int  _ldap_sasl_interact( )
{ return LDAP_SUCCESS;
}
   
int main()
{ 
  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;
  //初始化一个安全的上下文,后续的操作都是基于context,可以把他看成是基石
  krberr = krb5_init_context(&context);
  if (krberr) {
            //这个API能够解析错误码,分析出特定的错误信息,将其存储在errmsg中,然后打印到终端查看
            errmsg = krb5_get_error_message(NULL, krberr);
            printf("Err: Kerberos context initialization failed -> %s\n", errmsg);
            goto cleanup;
        }
  //这个API是你用户的信息(第二个参数)转化成krb中的格式存储在kprincpw中
    用户信息的组成"<认证的用户名>@<AD服务器的域名>"
  krberr = krb5_parse_name(context, "krblinlin@CTP.NET", &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");
  //使用用户的信息,密码换取TGT票据,并且拿到将凭证放my_creds中,但是这个凭证不是TGT,而是最后一次认证使用的
  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;
  
  //从内存中获取TGT票据,将其放入到ccache的变量中,便于后面换取TGS票据,就是服务器的最终通行证
  krberr = krb5_cc_resolve(context, "MEMORY:dhcp_ld_krb5_cc", &ccache);
  //从/tmp/krb5cc_1000中获取TGT票据,这个文件是自动生成的,将其存储到ccache中便于后面换取TGS票据,安全性上看来,确实没有从内存中获取TGT票据更安全,因为这个是可视化的,你甚至可以自己打开查看
  //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;
        }     
   
  //初始化ccache,相当于清空,但是你的ccache却不能是空的,我怀疑这个函数内存做的处理是,先是利用ccache中存储的TGT票据生成TGS服务票据,然后再清空。这里也能看见API还指定了用户名,说明生成的TGS是有对应关系的。
  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;
        }  
  //这个函数的作用我是有点迷的,这里又将my_creds的凭据放到ccache中又进行了一次认证,因为kerberos的认证是繁琐的,多次认证,我暂且这样理解吧,因为通过抓包发现也是进行了多次response
  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");

2.将LDAP句柄指针和用户信息绑定

        因为后续需要使用LDAP协议去管理AD服务器,例如增删改查用户的信息,例如我就进行了搜索用户邮箱的操作,这里没看大佬代码,因为我觉得我的更利于直观理解。

LDAP *ld;
  int rc;
  unsigned long version = LDAP_VERSION3;    //使用LDAP的v3版本

  //初始化一个LDAP句柄指针ld,后面的操作全是基于LDAP句柄指针,和上分的context差不多
  if (( rc = ldap_initialize(&ld,"ldap://192.168.1.11/")) != LDAP_SUCCESS)   //#12
  //if (( rc = ldap_initialize(&ld,"ldap://127.0.0.1/")) != LDAP_SUCCESS)
  {
    return(1);
  }
  //与服务器协商使用V3版本吗
  rc = ldap_set_option(ld,LDAP_OPT_PROTOCOL_VERSION,(void*)&version);

  //除了第6个参数其他都可以默认,第六个参数就是上分提到返回LDAP_SUCCESS的回调函数,这里大佬的代码是有一点问题的,因为它回调函数是返回int类型的,然而这里的参数的数据类型是一个宏定义,我这里看不了,下次补充进来,最后解决办法是将回调函数的返回类型换成这个宏定义,最后转换一下就行了。到这里就bind成功了,完成第四步的配置文件后,可以进行增删改查了
  if ((rc=ldap_sasl_interactive_bind_s(ld,NULL,"GSSAPI",NULL,NULL,LDAP_SASL_AUTOMATIC, _ldap_sasl_interact,NULL) ) != LDAP_SUCCESS)
  { printf ("Error: %s\n",ldap_err2string (rc));
    char *msg=NULL;
    ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg);
    printf ("Additional info: %s\n", msg);
    ldap_memfree(msg);
    return(1);
  }
  
  LDAPMessage *res;
  if ((rc=ldap_search_ext_s(ld,"",LDAP_SCOPE_ONELEVEL,NULL,NULL,0,NULL,NULL,NULL,0,&res)!=LDAP_SUCCESS))
  { printf("ldap_search  failed with 0x%x.\n",rc);         
    return(1);
  }
  printf("Success!\n");
  LDAPMessage *entry = ldap_first_entry( ld, res );
  int entry_count = ldap_count_entries(ld, res);
  for (int i = 0 ; i < entry_count; i++)
  { printf("dn: %s\n",ldap_get_dn(ld, entry));
    BerElement * ber;
    char * attribute = ldap_first_attribute(ld,entry, &ber);
    while(attribute)
    { printf ("attribute = %s\n",attribute);
      attribute = ldap_next_attribute(ld,entry, ber);
    }
    ber_free(ber,0);
    entry = ldap_next_entry(ld, entry);   
  }  
//--^--

cleanup:
    if (ccache) krb5_cc_close(context, ccache);  //#13
    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;   
}

四:环境的配置

第1步.在/etc/krb5.conf中添加服务器对应的信息(ubuntu)

[libdefaults]
  default_realm = TEST.ORG      //TEST.ORG 你的服务器域名,大写
  dns_lookup_realm = false
  dns_lookup_kdc = false
  ticket_lifetime = 24h
  renew_lifetime = 7d
  forwardable = true
[realms]
  TEST.ORG = {                          //填充你TEST.ORG的模块信息
    kdc = 192.168.45.141             //kdc的IP,在AD中,是集成在一块,填AD的IP即可
   admin_server = 192.168.45.141   //与kdc同理
   default_domain=TEST.ORG    //可填可不填
  }

[domain_realm]            //这个模块的信息可填可不填
  .test.org = TEST.ORG
  test.org = TEST.ORG

第2步.在/etc/hosts文件中添加KDC主机名和KDC所在域名(ubuntu)

27.0.0.1       localhost
127.0.1.1       ubuntu.test.org ubuntu

192.168.45.xxx  DC.TEST.ORG     #红色为添加信息,前面填充kdc IP ,后面填写  服务器主机名.域名 例如我的主机名为DC,域名为TEST.ORG
# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

如果第1步不配置,会找不到服务器;

如果第2步不配置,会出现下列错误:SASL(-1): generic failure: GSSAPI Error: Unspecified GSS failure. Minor code may provide more information (Server not found in Kerberos database)

同时你也可以在AD中添加DNS反向查询解决这个错误,而不修改/etc/hosts,当然这是用在用户基数很大的情况下,改变AD服务器可以一劳永逸,详情查看第3步。

第3步 为AD服务器添加DNS反向查询解决这个错误

3.1进入服务管理器,找到DNS,右击右侧服务器,进入DNS管理器

 3.2 右击反向查找区域,点击新建区域

3.3 一直点击下一步,直到输入IP的页面,输入AD 服务器IP的前3个字段 例如我的IP为192.168.45.141,那么输入192.168.45,再一直点击下一步,直到结束。 

 3.4 右击新生成的区域,点击新建指针

 3.5 填写AD服务的IP地址,先关闭

 3.6 找到正向查找区域->域名->打开dc(你的是你自己的域名,我的是dc)

3.7  找到FQDN,将其复制到2.3.5主机名的地方即可。

3.8 ubuntu打开/etc/reslov.conf,修改nameserver的地址 为你AD的地址,再将你ubuntu的DNS设置为AD的IP,否则这个文件的值会一直变动

4.结言

        直到这里,整个流程吗,包括配置文件全部结束,加油 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1205690.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据运营基础:用户场景营销

一、概述 场景营销模型是顶层模型&#xff0c;是站在用户经营和用户场景角度来制定经营策略的模型。本质上&#xff0c;场景营销模型是在用户使用产品的每个细分场景中通过分析用户需求整合功能、实体和体验等为用户提供服务的模型。 二、场景的起源和特点 数据运营体系在发展…

【C++】日期类实现,与日期计算相关OJ题

文章目录 日期类的设计日期计算相关OJ题HJ73 计算日期到天数转换KY111 日期差值KY222 打印日期KY258 日期累加 在软件开发中&#xff0c;处理日期是一项常见的任务。为了方便地操作日期&#xff0c;我们可以使用C编程语言来创建一个简单的日期类。在本文中&#xff0c;我们将介…

[工业自动化-18]:西门子S7-15xxx编程 - 软件编程 - PLC用于工业领域的嵌入式系统:硬件原理图、指令系统、系统软件架构、开发架构等

目录 前言&#xff1a; 一、PLC的硬件电路原理 1.1 硬件框图 1.2 硬件模块详解 &#xff08;1&#xff09;CPU &#xff08;2&#xff09;存储器 &#xff08;3&#xff09;输入/输出&#xff08;I/O&#xff09;模块 &#xff08;4&#xff09;编程器 &#xff08;5&a…

(只需三步)Vmvare tools安装教程,实现与windows互通复制粘贴与文件拖拽

首先确保Ubuntu是联网的&#xff0c;如果连不上网可以参考我的这个联网教程&#xff0c;也很简单 &#xff08;只需三步&#xff09;虚拟机上vm的ubuntu不能联上网怎么办-CSDN博客 第一步&#xff1a;卸载之前的tools,确保没有残留 sudo apt-get autoremove open-vm-tools 第…

第2关:计算二叉树的深度和节点个数

任务描述相关知识 二叉树深度概念二叉树节点二叉树叶子节点概念编程要求测试说明 任务描述 本关任务&#xff1a;给定一棵二叉树&#xff0c;计算该二叉树的深度、总节点个数和叶子节点个数。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.二叉树深度概念…

Linux高级编程:IPC之管道

一、无名管道 1.1 无名管道的概述 管道(pipe)又称无名管道。 无名管道是一种特殊类型的文件&#xff0c;在应用层体现为两个打开的文件描述符。 任何一个进程在创建的时候&#xff0c;系统都会 给他分配4G的虚拟内存&#xff0c;分为3G的用户空间和1G 的内核空间&#xff0c;内…

SOME/IP学习笔记2

1. SOME/IP 协议 SOME/IP目前支持UDP&#xff08;用户传输协议&#xff09;和TCP&#xff08;传输控制协议&#xff09;&#xff0c; PS:UDP和TCP区别如下 TCP面向连接的&#xff0c;可靠的数据传输服务&#xff1b;UDP面向无连接的&#xff0c;尽最大努力的数据传输服务&…

springboot容器

1.主要指的是servlet容器 servlet组件由sevlet Filter Listener等 2.自动配置原理 通过ServletWebServerFactoryAutoConfiguration 配置这些内容 (自动配置类开始分析功能) conditionalOnclass开启条件 ServletRequest类 import导入嵌入式的tomcat Jetty等 这些是配置类&…

鸿蒙原生应用开发-DevEco Studio中HarmonyOS与OpenHarmony项目的切换

一、找到该目录 二、修改操作系统类型 三、分别进行开发&#xff0c;一些常规的应用功能实现后&#xff0c;相互切换后都可以正常运行的。前期OpenHarmony项目如果连接开发板比较困难的化&#xff0c;开发完成后&#xff0c;切换成为HarmonyOS后就可以比较详细地看看效果了。

接口开放太麻烦?试试阿里云API网关吧

前言 我在多方合作时&#xff0c;系统间的交互是怎么做的&#xff1f;这篇文章中写过一些多方合作时接口的调用规则和例子&#xff0c;然而&#xff0c;接口开放所涉及的安全、权限、监控、流量控制等问题&#xff0c;可不是简简单单就可以解决的&#xff0c;这一般需要专业的…

高防IP可以抵御哪些恶意攻击

高防IP协议可以隐藏用户的站点&#xff0c;使得攻击者无法发现恶意攻击的目标网络资源&#xff0c;从而提高了源站的安全性。能够有效抵御常见的恶意攻击类型ICMPFlood、UDPFlood、 TCPFlood、SYNFlood、ACKFlood等&#xff0c;帮助游戏、金 融、电子商务、互联网、政企等行业抵…

套接字的多种可选项

套接字可选项和I/O缓冲大小 套接字的多种可选项 套接字可选项分为 IPPROTO_IP、IPPROTO_TCP、SOL_SOCKET 三层&#xff0c;各层的含义为&#xff1a; IPPROTO_IP&#xff1a;IP 协议相关事项&#xff1b; IPPROTO_TCP&#xff1a;TCP 协议相关事项&#xff1b; SOL_SOCKET&am…

HCIE-灾备技术和安全服务

灾备技术 灾备包含两个概念&#xff1a;容灾、备份 备份是为了保证数据的完整性&#xff0c;数据不丢失。全量备份、增量备份&#xff0c;备份数据还原。 容灾是为了保证业务的连续性&#xff0c;尽可能不断业务。 快照&#xff1a;保存的不是底层块数据&#xff0c;保存的是逻…

算法笔记—第五章-最大公约数与最小公倍数

算法笔记-最大公约数与最小公倍数 最大公约数最小公倍数最大公约数 2最小公倍数2互质互质2 最大公约数 //最大公约数与最小公倍数#include <cstdio> int gcd(int a, int b) {if (b 0) //截止的条件{return a; }else {return gcd(b, a % b);//这里是a与b和b&#xff…

车载通信与DDS标准解读系列(1):DDS-RPC

▎RPC & DDS-RPC RPC&#xff1a;Remote Procedure Call&#xff0c;远程过程调用。 远程过程调用是一种进程间通信&#xff0c;它允许计算机程序在另一个地址空间中执行子程序&#xff0c;就好像用别人的东西像用自己的一样&#xff0c;常用于分布式系统。 远程过程调用…

GCN代码讲解

这里写的有点抽象&#xff0c;所以具体的可以参照下面代码块中的注释&#xff1a; def load_data(path"../data/cora/", dataset"cora"):"""Load citation network dataset (cora only for now)"""print(Loading {} datase…

Spark数据倾斜优化

1 数据倾斜现象 1、现象 绝大多数task任务运行速度很快&#xff0c;但是就是有那么几个task任务运行极其缓慢&#xff0c;慢慢的可能就接着报内存溢出的问题。 2、原因 数据倾斜一般是发生在shuffle类的算子&#xff0c;比如distinct、groupByKey、reduceByKey、aggregateByKey…

U-Mail邮件中继有效解决海外邮件发送不畅难题

相信不少企业都经历过类似的问题&#xff0c;在跟国外客户发送电子邮件的过程中&#xff0c;经常会遇到邮件发不过去、邮件隔了很久对方才收到&#xff0c;或者是邮件退信等情况出现。对此&#xff0c;U-Mail技术专家李工解释到&#xff0c;导致海外通邮不畅主要有以下三个原因…

ubuntu18.04安装google浏览器

下载google安装包 wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb 安装google浏览器 sudo dpkg -i google-chrome-stable_current_amd64.deb 执行安装 sudo apt-get -f install 启动浏览器 在应用程序中找到google图标点击运行

密钥安全存储方案探讨与实践

随着信息技术的迅猛发展和应用范围的不断扩大&#xff0c;我们日常生活中的许多方面已经与信息技术密不可分。而在信息安全领域中&#xff0c;密钥的安全存储显得尤为重要。本文将探讨密钥安全存储的必要性、相关技术和实践方案&#xff0c;并提出一些解决方案。 一、密钥安全存…