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 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.mzph.cn/news/141895.shtml

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

相关文章

经典文献阅读之--Fast and Robust Ground Surface Estimation...(均匀B样条采样快速估计地平面)

0. 简介 对于激光雷达的地面估计分割&#xff0c;目前其实有很多方法做了快速并鲁棒的分割&#xff0c;比如说我们之前写的一篇《经典文献阅读之–FEC》一文中就给出了快速分割的方案&#xff0c;当中第一步就是需要对地面进行分割。而我们这次看的是一篇使用均匀B样条的方法来…

H5三网魔幻手游【众神传奇】win服务端+GM授权后台+架设教程

搭建资源下载地址&#xff1a;H5三网魔幻手游【众神传奇】win服务端GM授权后台架设教程-海盗空间

C++ 类型转换static_cast、dynamic_cast、const_cast、reinterpret_cast用法以及区别

在C++中,有四种主要的类型转换操作符:static_cast、dynamic_cast、const_cast、reinterpret_cast。它们各自用于不同的转换场景,并有不同的行为和限制。 static_cast: 用于进行静态类型转换,主要用于相关类型之间的转换,例如数值类型之间的转换、基类到派生类的转换(在安…

(八)Spring源码解析:Spring MVC

一、Servlet及上下文的初始化 1.1> DispatcherServlet的初始化 对于Spring MVC来说&#xff0c;最核心的一个类就是DispatcherServlet&#xff0c;它负责请求的行为流转。那么在Servlet的初始化阶段&#xff0c;会调用init()方法进行初始化操作&#xff0c;在DispatcherSe…

【Python3】【力扣题】268. 丢失的数字

【力扣题】题目描述&#xff1a; 【Python3】代码&#xff1a; 1、解题思路&#xff1a;哈希。元素去重&#xff0c;依次判断是否在0-n内&#xff0c;没有则返回。 知识点&#xff1a;set(...)&#xff1a;转为集合&#xff0c;集合中的元素不重复。 class Solution:def mis…

Angular 使用教程——基本语法和双向数据绑定

Angular 是一个应用设计框架与开发平台&#xff0c;旨在创建高效而精致的单页面应用 Angular 是一个基于 TypeScript 构建的开发平台。它包括&#xff1a;一个基于组件的框架&#xff0c;用于构建可伸缩的 Web 应用&#xff0c;一组完美集成的库&#xff0c;涵盖各种功能&…

OpenCV图像坐标系

绘制代码: X轴 # 选取两个点 point1 = (20, 0) point2 = (200, 0)# 在图像上绘制连接线 cv2.line(img, point1, point2, (

2023.11.12使用flask对图片进行黑白处理(base64编码方式传输)

2023.11.12使用flask对图片进行黑白处理&#xff08;base64编码方式传输&#xff09; 由前端输入图片并预览&#xff0c;在后端处理图片后返回前端显示&#xff0c;可以作为图片处理的模板。 关键点在于对图片进行base64编码的转化。 使用Base64编码可以更方便地将图片数据嵌入…

java8 : Collectors.groupingBy(分组)

Collectors.groupingBy配合Stream流使用&#xff0c;可以对集合中一个或多个属性进行分组&#xff0c;分组后还可以做聚合运算。 首先把数据放入集合&#xff1a; Product prod1 new Product(1L, 1, new BigDecimal("15.5"), "面包", "零食");…

低代码与传统开发:综合比较

近年来&#xff0c;低代码开发作为软件开发的趋势获得了显着的发展势头。根据 MarketsandMarkets 的数据&#xff0c;低代码开发市场预计将实现 28.1% 的大幅增长率&#xff0c;到 2025 年价值将达到 455 亿美元。这一显着增长表明了各行业和企业对低代码平台的需求和采用不断增…

2023前端流行的新技术

作为2023年之前的技术水平有限&#xff0c;以下是一些目前为止较为热门的前端开发技术和趋势&#xff0c;这些技术可能在2023年之前进一步发展和普及。 前端程序员可以考虑学习和掌握以下技术&#xff1a; 1.Vue 3和React Hooks&#xff1a;Vue.js和React是目前最受欢迎的JavaS…

人工智能基础_机器学习027_L2正则化_岭回归_非稀疏性_原理解读_公式推导---人工智能工作笔记0067

然后我们再来看一下岭回归,也就是第二范数对吧, 他的公式,平方以后,加和然后开平方.L2的公式是 可以看到L2公式,也是有个阿尔法,惩罚项对吧. 可以看到因为L2带有平方,所以他的图形是个圆形 我们可以把L2范数,进行画出来看看 这里我们先看L2的公式,这里我们让 这个公式写成1 …

2023-11-14 mysql-主从复制-重置主从连接-记录

摘要: mysql的主从复制, 当从库执行binlog出错后, 会中止主从复制. 此时需要重置主从连接, 以重建主从关系. 主库操作: 一. 清理同步的数据库 drop database test;二. 重置主库状态 reset master;reset slave all;三. 检测主库状态 show master status;mysql> show master…

【已解决】vscode 配置C51和MDK环境配置

使用命令 gcc -v -E -x c - 看自己gcc 有没有安装好 也可以在自己的vscode中新建一个终端 gcc -v g -v 首先把自己的C51 和MDK 路径 设置好 vscode 中设置 C51 和 MDK 的路径 这是你keil 中写 51单片机和 STM32 的 如果你出现什么include 的什么波浪线&#xff0c;那估计…

2023亚太杯数学建模B题思路解析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料5 最后 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 2023年第十三…

基于FPGA的图像RGB转HLS实现,包含testbench和MATLAB辅助验证程序

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1计算最大值和最小值 4.2计算亮度L 4.3计算饱和度S 4.4计算色调H 5.算法完整程序工程 1.算法运行效果图预览 将FPGA结果导入到MATLAB显示效果&#xff1a; 2.算法运行软件版本 Vivado…

react hook ts 实现 列表的滚动分页加载,多参数混合混合搜索

InfiniteScroll 的组件见&#xff1a; https://blog.csdn.net/Zhooson/article/details/134396945 search.tsx 页面 import { FC, useEffect, useState } from react import InfiniteScroll from ../../components/InfiniteScrollconst tabs [{id: 1,title: tab-1,index: 1…

【python】选数

题目&#xff1a; """ 题目描述&#xff1a; 给定一个由 n 个整数组成的序列x_1, x_2,..., x_n)&#xff0c;以及一个整数 k ( k < n )。从这 n 个整数中选择 k 个整数相加&#xff0c;可以得到多种不同的和。例如&#xff0c;当 n 4&#xff0c;k 3&#…

国际阿里云:云服务器灾备方案!!!

保障企业业务稳定、IT系统功能正常、数据安全十分重要&#xff0c;可以同时保障数据备份与系统、应用容灾的灾备解决方案应势而生&#xff0c;且发展迅速。ECS可使用快照、镜像进行备份。 灾备设计 快照备份 阿里云ECS可使用快照进行系统盘、数据盘的备份。目前&#xff0c;阿…

快手自动引流软件的运行分享,以及涉及到技术与核心代码分享

先来看实操成果&#xff0c;↑↑需要的同学可看我名字↖↖↖↖↖&#xff0c;或评论888无偿分享 一、引言 引流是任何网络创业者或营销人员必备的技能之一。手动引流不仅耗时&#xff0c;而且效果难以保证。因此&#xff0c;自动引流软件应运而生&#xff0c;成为许多人的得力助…