Linux上C++通过LDAP协议使用kerberos认证AES加密连接到AD服务器
一.前言
记录自己在实现这个流程遇到的各种问题,因为我也是看了许多优质的文章以及组内大佬的帮助下才弄成的,这里推荐一个大佬的文章,写的非常优秀,比我这篇文章写得好得很多,最后我也是看这个大佬的代码最终才实现的,特别鸣谢。看这篇文章前提你得足够的了解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");//--vkrb5_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的认证是繁琐的,多次认证,我暂且这样理解吧,因为通过抓包发现也是进行了多次responsekrberr = 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); //#13if (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 = falsedns_lookup_kdc = falseticket_lifetime = 24hrenew_lifetime = 7dforwardable = 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.ORGtest.org = TEST.ORG
第2步.在/etc/hosts文件中添加KDC主机名和KDC所在域名(ubuntu)
27.0.0.1 localhost
127.0.1.1 ubuntu.test.org ubuntu192.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.结言
直到这里,整个流程吗,包括配置文件全部结束,加油 。
相关文章:

Linux上C++通过LDAP协议使用kerberos认证AES加密连接到AD服务器
一.前言 记录自己在实现这个流程遇到的各种问题,因为我也是看了许多优质的文章以及组内大佬的帮助下才弄成的,这里推荐一个大佬的文章,写的非常优秀,比我这篇文章写得好得很多,最后我也是看这个大佬的代码最终才实现的…...

开源供应链管理系统 多供应商批发管理系统方案及源码输出
开发框架:PHPMySQL 后端框架:ThinkPHP 订货端:PC小程序 客户订货端:小程序 多仓库OR多供应商:多供应商 是否进销存:自带进销存 整个方案含B端订货PC、小程序端、C端小程序端下单,源码&…...
2yocto 自启动程序(服务)
yocto 自动运行主程序 文章目录 yocto 自动运行主程序1 问题现象2 问题分析:1)是否执行2)查看服务状态11)自动22)手动3)rc.local服务3 解决之道创建自定义服务自定义服务运行设置关系服务参考1 问题现象 系统启动后,自定义的主程序没有随着启动的起动,自动运行起来(界…...

AI 绘画 | Stable Diffusion 进阶 Embeddings(词嵌入)、LoRa(低秩适应模型)、Hypernetwork(超网络)
前言 Stable Diffusion web ui,除了依靠文生图(即靠提示词生成图片),图生图(即靠图片提示词生成图片)外,这两种方式还不能满足我们所有的绘图需求,于是就有了 Embeddings࿰…...

【汇编】计算机的组成
文章目录 前言一、计算机的基本组成1.1 中央处理器(CPU)1.2 内存指令和数据存储的位置计算机中的存储单元计算机中的总线地址总线数据总线控制总线 1.3 输入设备和输出设备1.4 存储设备 二、计算机工作原理三、计算机的层次结构总结 前言 计算机是现代社…...

asp.net学生宿舍管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
一、源码特点 asp.net 学生宿舍管理系统是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为sqlserver2008,使用c#语言 开发 asp.net学生宿舍管理系统1 应用技…...

[C++]Leetcode17电话号码的字母组合
题目描述 解题思路: 这是一个深度优先遍历的题目,涉及到多路递归,下面通过画图和解析来分析这道题。 首先说到的是映射关系,那么我们就可以通过一个字符串数组来表示映射关系(字符串下标访问对应着数字映射到对应的…...
OpenBMC Uboot下使用TFTP升级系统
设置TFTP服务器 setenv serverip 192.168.2.300 传入系统固件 OpenBMC的.bin文件:image-bmc,位于obmc-phosphor-image-XXXXX.static.mtd.all.tar压缩包中, image-bmc解压到TFTP服务器共享目录,传入固件: #直接输入…...

巨量千川「全域推广」指南来袭!助力商家开拓新流量
如今,在抖音上进行直播销售的商家,都希望在不影响ROI的情况下,提高整体业务水平,实现高效率的结果。然而,考虑到人货场波动和直播本身的复杂性,许多商家面临着诸如低投放效果、波动的ROI和缺乏GMV增长动力等…...

视频剪辑助手:轻松实现视频随机分割并提取音频保存
随着社交媒体和视频平台的日益普及,视频制作和分享已成为人们日常生活的一部分。无论是分享个人生活,还是展示才艺,或是推广产品,视频都是一个非常有效的工具。但是,视频制作往往涉及到大量的视频剪辑工作,…...
java注解的作用
注解 Java注解是一种用于给类、方法、字段、参数等元素添加元数据的机制,可以用来实现一些特定的功能,比如配置、文档、测试等。Java注解有两种类型:内置的和自定义的。内置的注解是Java语言或者JDK提供的,比如Override, Depreca…...
css中的hover用法示例(可以在vue中制作鼠标悬停显示摸个按钮的效果)
css中的hover 1、hover的定义 hover选择器用于选择鼠标指针浮动在上面的元素,它适用于所有元素,可以用来实现类似于js的一些功能。 2.hover的作用 css中hover属性,鼠标移到上面的时候可以激活,它可以实现悬浮元素上改变样式&a…...

labview实现仪器的控制visa
*IDN? 是识别大多数仪器的查询指令。仪器会回应一个用于描述仪器的识别字符串。如果仪器不接受该指令,请在仪器手册中查询仪器能识别的指令列表。 如下图所示: 程序如下:...
说说React Router有几种模式?实现原理?
一、是什么 在单页应用中,一个web项目只有一个html页面,一旦页面加载完成之后,就不用因为用户的操作而进行页面的重新加载或者跳转,其特性如下: 改变 url 且不让浏览器像服务器发送请求在不刷新页面的前提下动态改变浏览器地址栏中的URL地址其中主要分成了两种模式: has…...
laravel5+版本aes128加解密
使用场景: aes/cbc/pkcs5padding/128加解密 EncryptService.php代码示例如下 namespace App\Services;/*** aes/cbc/pkcs5padding/128加解密*/ class EncryptService {//加密方法private static $sDefaultEncMethod AES-128-CBC;//默认key值-自定义16位字符串长度…...

Spark的转换算子和操作算子
1 Transformation转换算子 1.1 Value类型 1)创建包名:com.shangjack.value 1.1.1 map()映射 参数f是一个函数可以写作匿名子类,它可以接收一个参数。当某个RDD执行map方法时,会遍历该RDD中的每一个数据项,并依次应用f函…...

传奇手游天花板赤月【盛世遮天】【可做底版】服务端+自主授权+详细教程
搭建资源下载地址:传奇手游天花板赤月【盛世遮天】【可做底版】服务端自主授权详细教程-海盗空间...
TP触摸屏调试
此处以MT6739 1g版本敦泰TP为例(kernel 4.19),主要修改点如下: 1. 两个配置文件defconfig: kernel-4.19\arch\arm\configs\k39tv1_bsp_1g_k419_debug_defconfig: kernel-4.19\arch\arm\configs\k39tv1_bsp_1g_k419_defconfig: CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCRE…...

11-13 spring整合web
spring注解 把properties文件中的key注入到属性当中去 xml配置文件拆分 -> import标签 注解开发中 import 实现 搞一个主配置类,其他配置类全部导入进来这个这个主配置类 而且其他配置类不需要 加上configuration注解 之前这个注解用于表示这是一个配置文件 …...

基于C#开发的任天堂 Switch 开源模拟器
今天给大家推荐一款基于C#开发的任天堂 Switch 开源模拟器,可方便开发人员来测试游戏,也用于娱乐。 01 项目简介 Ryujinx 是一个开源的任天堂 Switch 模拟器,可以在 PC 上模拟运行 Switch 游戏。采用C#开发,基于 .NET Core技术框…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...