濟(jì)寧網(wǎng)站建設(shè)優(yōu)化百度網(wǎng)盤(pán)登錄入口 網(wǎng)頁(yè)
問(wèn)題由來(lái)
客戶需求計(jì)劃列入支持第三方帳號(hào)系統(tǒng),包括Web賬號(hào)。需求來(lái)源是用戶想要用它們的帳號(hào)直接登錄Linux Deepin操作系統(tǒng)。一個(gè)失敗的實(shí)現(xiàn)方案是用戶以較小的成本改造帳號(hào)管理系統(tǒng)發(fā)布HTTP服務(wù),我們開(kāi)發(fā)一個(gè)PAM模塊與Web服務(wù)器交互,數(shù)據(jù)格式化采用JSON。結(jié)果遇到su提示帳號(hào)不存在的問(wèn)題。在Linux Deepin系統(tǒng)登錄界面、通過(guò)技術(shù)手段進(jìn)入桌面后發(fā)現(xiàn)鎖屏界面無(wú)法解鎖等諸多問(wèn)題。通過(guò)程序驗(yàn)證和su源代碼分析驗(yàn)證,此方案最大的局限性是su在識(shí)別到用戶的信息之前不會(huì)執(zhí)行PAM模塊。此方案只能用程序調(diào)用pam_authenticate觸發(fā)PAM模塊的執(zhí)行。
Linux Name Service Switch
Linux NSS (Name Service Switch) 是一種在 Linux 系統(tǒng)中實(shí)現(xiàn)域名解析,用戶認(rèn)證和授權(quán)等功能的模塊化系統(tǒng)。它提供了一種靈活的方式來(lái)配置系統(tǒng)如何查找用戶,組,密碼和其他網(wǎng)絡(luò)資源的信息。
NSS 的核心概念是所謂的 “Switch”,這是一組可插入的模塊,它們根據(jù)一定的順序來(lái)處理不同類型的請(qǐng)求。用戶可以通過(guò)編輯 /etc/nsswitch.conf 文件來(lái)配置這些模塊的加載順序,以達(dá)到滿足自己特定需求的目的。例如,您可以通過(guò) NSS 配置更改系統(tǒng)的驗(yàn)證源,以便使用 LDAP 或 Kerberos 等網(wǎng)絡(luò)身份驗(yàn)證服務(wù),而不是使用本地 /etc/passwd 文件。
Linux NSS 可以支持多種不同的數(shù)據(jù)源,包括本地文件、NIS、LDAP、Kerberos 等。這些模塊還可以針對(duì)特定服務(wù)或應(yīng)用程序進(jìn)行定制化,以實(shí)現(xiàn)更高效的查詢和更安全的身份驗(yàn)證和授權(quán)。使用 Linux NSS,系統(tǒng)管理員可以更靈活地管理 Linux 系統(tǒng),滿足不同用戶對(duì)系統(tǒng)資源和服務(wù)的需求。
本文將詳細(xì)講解開(kāi)發(fā)過(guò)程,實(shí)現(xiàn)Web賬號(hào)登錄Linux桌面系統(tǒng)。
關(guān)于getpwnam函數(shù)
NSS的作用是識(shí)別身份信息。傳統(tǒng)的用戶名和密碼驗(yàn)證方式,身份信息是一個(gè)字符串。NSS通過(guò)調(diào)用這個(gè)方法尋找這個(gè)字符串對(duì)應(yīng)到系統(tǒng)中的用戶,獲取它關(guān)聯(lián)的UID和GID,從而對(duì)它進(jìn)行管理。Oracle官方文檔原話:
The name service switch is a file named nsswitch.conf. It controls how a client machine or application obtains network information. It is used by client applications that call any of the getXbyY() interfaces such as the following.gethostbyname()
getpwuid()
getpwnam()
getipnodebyname()
getpwnam
不是直接讀取讀取/etc/passwd
或者/etc/shadow
文本文件,它取決于nsswitch.conf
中passwd
這一行的配置。對(duì)于Linux Deepin操作系統(tǒng)如果配置了files
或者compat
,那么最終由libnss_files-2.28.so
或者libnss_compat-2.28.so
讀取這幾個(gè)配置文件,程序從getpwnam
運(yùn)行到了_nss_files_getpwnam_r
或者_nss_compat_getpwnam_r
。
NSS釋義
這里的NSS指的是Linux Name Service Switch
,不是Linux Network Security Service
。兩者對(duì)Linux都很重要,資料都很稀缺。我們可以理解為名字解析服務(wù),它實(shí)現(xiàn)把外部輸入的用戶信息與系統(tǒng)中的用戶信息關(guān)聯(lián)。它按照/etc/nsswitch.conf
指定的順序逐個(gè)模塊調(diào)用,如果找到了,就立即返回libc,su根據(jù)返回的用戶信息啟動(dòng)PAM流程,進(jìn)行身份驗(yàn)證。
nsswitch配置文件格式
一行寫(xiě)一種類型的配置,每行以類型名稱加冒開(kāi)頭,以NSS模塊名稱列表結(jié)尾,多個(gè)NSS模塊名稱以空格隔開(kāi)。NSS總共支持16種類型的配置。常見(jiàn)的有passwd
、group
、shadow
、gshadow
、hosts
、networks
、protocols
、rpc
、netgroup
等等。其中passwd
類別實(shí)現(xiàn)用戶身份識(shí)別。模塊與類別不是一對(duì)一的關(guān)系,libc給每個(gè)類型別都定義了一套接口,這個(gè)接口函數(shù)名通常以_r
結(jié)尾,比如getpwuid_r
。NSS模塊的實(shí)現(xiàn)是動(dòng)態(tài)鏈接庫(kù),文件名必須以libnss_
為前綴,以.so
或.so.2
為后綴,中間部分就是模塊名稱,比如libnss_mjaw.so
,它的模塊名稱就是mjaw
。因它實(shí)現(xiàn)了passwd
、group
、shadow
、gshadow
、hosts
、networks
、rpc
、protocols
這些類別的接口,所以配置文件中這些類型對(duì)應(yīng)的行都可以加上mjaw
這個(gè)模塊名稱。
開(kāi)發(fā)
引用Petzold Charles先生的一句名言: Do not call me, I will call you 。NSS程序設(shè)計(jì)須深刻理解這句話,下面的每一個(gè)函數(shù)都不是開(kāi)發(fā)者要調(diào)用的函數(shù),而是系統(tǒng)用戶態(tài)邊界一定會(huì)調(diào)用你的函數(shù)。
NSS模塊要求開(kāi)發(fā)者采用C語(yǔ)言,Qt代碼無(wú)法在PAM和NSS模塊的上下文環(huán)境中運(yùn)行。在PAM和NSS模塊編程中采用C與C++混合編程的方式對(duì)于Qt來(lái)說(shuō)有很多問(wèn)題需要解決,其它的框架暫未嘗試。對(duì)于HTTP,可以用cURL。對(duì)于Json解析,可采用cJSON,對(duì)于密碼加密,可采用mHash。
認(rèn)證數(shù)據(jù)的存儲(chǔ)
NSS模塊須自行管理認(rèn)證數(shù)據(jù)。因此首先建立一個(gè)鏈表.
重要的頭文件
#include "passwd_list.h"
#include "malloc.h"
#include <pwd.h>
#include <string.h>#include <cjson/cJSON.h>
#include <mhash.h>
#include <curl/curl.h>
創(chuàng)建鏈表
MJAW_INTERNAL pmjaw_passwd_list_t passwd_create()
{pmjaw_passwd_list_t node = (pmjaw_passwd_list_t)calloc(sizeof(mjaw_passwd_list_t), 1);return node;
}
創(chuàng)建認(rèn)證賬號(hào)
MJAW_INTERNAL passwd_t passwd_create2()
{passwd_t pwd = (passwd_t)calloc(sizeof(struct passwd), 1);pwd->pw_name = (char *)calloc(UINT8_MAX, 1);pwd->pw_gecos = (char *)calloc(UINT8_MAX, 1);pwd->pw_shell = (char *)calloc(UINT8_MAX, 1);pwd->pw_dir = (char *)calloc(UINT8_MAX, 1);pwd->pw_passwd = (char *)calloc(UINT8_MAX, 1);
}
釋放鏈表
MJAW_INTERNAL void passwd_free(pmjaw_passwd_list_t head)
{pmjaw_passwd_list_t each = head->next;while (each) {pmjaw_passwd_list_t del = each;each = each->next;passwd_free2(del->data);free(del);}free(head);
}
釋放認(rèn)證賬號(hào)
MJAW_INTERNAL void passwd_free2(passwd_t data)
{free(data->pw_name);free(data->pw_gecos);free(data->pw_shell);free(data->pw_dir);free(data->pw_passwd);free(data);
}
獲取指定索引位置的用戶對(duì)象
/*** @brief 獲取指定索引位置的用戶對(duì)象** @param head* @param nindex* @return MJAW_INTERNAL*/
MJAW_INTERNAL passwd_t passwd_at(pmjaw_passwd_list_t head, int nindex)
{if (nindex >= passwd_size(head)) {return NULL;}pmjaw_passwd_list_t each = head->next;int neach = 0;while (each && neach < nindex) {each = each->next;neach++;}return each->data;
}
根據(jù)用戶名查找用戶對(duì)象
/*** @brief 根據(jù)用戶名查找用戶對(duì)象** @param head* @param username* @return MJAW_INTERNAL*/
MJAW_INTERNAL passwd_t passwd_find_id_by_username(pmjaw_passwd_list_t head, const char *username)
{pmjaw_passwd_list_t find = head->next;while (find) {if (strcmp(find->data->pw_name, username) == 0) {return find->data;}}return NULL;
}
根據(jù)用戶ID查找用戶對(duì)象
/*** @brief 根據(jù)用戶ID查找用戶對(duì)象** @param head* @param uid* @return MJAW_INTERNAL*/
MJAW_INTERNAL passwd_t passwd_find_username_by_id(pmjaw_passwd_list_t head, uint32_t uid)
{pmjaw_passwd_list_t find = head->next;while (find) {if (find->data->pw_uid == uid) {return find->data;}}return NULL;
}
獲取指定用戶ID的索引
/*** @brief 獲取指定用戶ID的索引** @param head* @param uid* @return MJAW_INTERNAL*/
MJAW_INTERNAL int passwd_indexof(pmjaw_passwd_list_t head, uint32_t uid)
{pmjaw_passwd_list_t each = head->next;int nindex = 0;int bfind = 0;while (each) {if (each->data->pw_uid == uid) {bfind = 1;break;}nindex++;}if (bfind) {return nindex;}return -1;
}
在隊(duì)列末尾增加一個(gè)用戶對(duì)象
/*** @brief 在隊(duì)列末尾一個(gè)用戶對(duì)象** @param head* @param data* @return MJAW_INTERNAL*/
MJAW_INTERNAL void passwd_append(pmjaw_passwd_list_t head, passwd_t data)
{// mjaw_log0(__FILE__, __LINE__, __func__, "enter");pmjaw_passwd_list_t end = head;while (end->next) {end = end->next;}pmjaw_passwd_list_t node = passwd_create();node->data = passwd_create2();node->data->pw_uid = data->pw_uid;node->data->pw_gid = data->pw_gid;strncpy(node->data->pw_name, data->pw_name, UINT8_MAX);strncpy(node->data->pw_gecos, data->pw_gecos, UINT8_MAX);strncpy(node->data->pw_shell, data->pw_shell, UINT8_MAX);strncpy(node->data->pw_dir, data->pw_dir, UINT8_MAX);strncpy(node->data->pw_passwd, data->pw_passwd, UINT8_MAX);node->previous = end;end->next = node;// mjaw_log0(__FILE__, __LINE__, __func__, "leave");
}
移除指定索引位置的用戶對(duì)象
/*** @brief 移除指定索引位置的用戶對(duì)象** @param head* @param nindex* @return MJAW_INTERNAL*/
MJAW_INTERNAL void passwd_remove(pmjaw_passwd_list_t head, int nindex)
{if (nindex >= passwd_size(head)) {return;}pmjaw_passwd_list_t remove = head->next;int neach = 0;while (remove && neach < nindex) {remove = remove->next;neach++;}remove->next->previous = remove->previous;remove->previous->next = remove->next;passwd_free2(remove->data);free(remove);
}
獲取隊(duì)列大小
/*** @brief 獲取隊(duì)列大小,HEAD本身不參與計(jì)算** @param head* @return MJAW_INTERNAL*/
MJAW_INTERNAL uint32_t passwd_size(pmjaw_passwd_list_t head)
{uint32_t nsize = 0;while (head = head->next) {nsize++;}return nsize;
}
賬號(hào)克隆
/*** @brief 用戶信息復(fù)制** @param from* @param to* @return MJAW_INTERNAL*/
MJAW_INTERNAL void passwd_copy(const passwd_t from, passwd_t to)
{memset(to, 0, sizeof(struct passwd));to->pw_name = (char *)calloc(UINT8_MAX, 1);to->pw_gecos = (char *)calloc(UINT8_MAX, 1);to->pw_shell = (char *)calloc(UINT8_MAX, 1);to->pw_dir = (char *)calloc(UINT8_MAX, 1);to->pw_passwd = (char *)calloc(UINT8_MAX, 1);to->pw_name = from->pw_name;to->pw_passwd = from->pw_passwd;to->pw_uid = from->pw_uid;to->pw_gid = from->pw_gid;to->pw_gecos = from->pw_gecos;to->pw_dir = from->pw_dir;to->pw_shell = from->pw_shell;strncpy(to->pw_name, from->pw_name, UINT8_MAX);strncpy(to->pw_gecos, from->pw_gecos, UINT8_MAX);strncpy(to->pw_shell, from->pw_shell, UINT8_MAX);strncpy(to->pw_dir, from->pw_dir, UINT8_MAX);strncpy(to->pw_passwd, from->pw_passwd, UINT8_MAX);
}
Web互通
Web賬號(hào)登錄Linux桌面的通訊環(huán)節(jié),假定存在一個(gè)RESTful服務(wù)http://127.0.0.1:1081提供json數(shù)據(jù)接口。
cURL發(fā)起請(qǐng)求
兼顧處理網(wǎng)絡(luò)錯(cuò)誤。
/*** @brief curl回調(diào),收集數(shù)據(jù)** @param buffer* @param size* @param nmemb* @param user_p* @return size_t*/
static size_t mjaw_curl_login_data(void *buffer, size_t size, size_t nmemb, void *user_p)
{char *wrapper = (char *)user_p;// mjaw_logv(__FILE__, __LINE__, __func__, "buffer size: %d, size: %d, nmemb: %d", strlen((char *)buffer), size, nmemb);strncat(wrapper, buffer, nmemb);return nmemb;
}static bool network_has_error(cJSON **httpContent)
{//執(zhí)行網(wǎng)絡(luò)請(qǐng)求查詢所有用戶char buffer[UINT16_MAX] = {0};CURL *pcurl = curl_easy_init();curl_easy_setopt(pcurl, CURLOPT_URL, "http://127.0.0.1:1081/users");curl_easy_setopt(pcurl, CURLOPT_TIMEOUT, 3);curl_easy_setopt(pcurl, CURLOPT_WRITEFUNCTION, mjaw_curl_login_data);curl_easy_setopt(pcurl, CURLOPT_WRITEDATA, buffer);CURLcode icode = curl_easy_perform(pcurl);//如果網(wǎng)絡(luò)請(qǐng)求失敗if (icode != CURLE_OK) {mjaw_log1i(__FILE__, __LINE__, __func__, " nss_cw can not access website. curl error code: %d.", icode);return true;}//把HTTP返回轉(zhuǎn)換成json對(duì)象cJSON *res = cJSON_Parse(buffer);//json對(duì)象必須是數(shù)組if (!cJSON_IsArray(res)) {// mjaw_logv(__FILE__, __LINE__, __func__, "parse http response error: %s", buffer);cJSON_Delete(res);return true;}*httpContent = res;return false;
}
cJSON庫(kù)解析賬號(hào)信息
/*** @brief 從指定的配置讀取Web服務(wù)器上的用戶列表并保存到列表** @param head* @return MJAW_INTERNAL*/
MJAW_INTERNAL void passwd_init(pmjaw_passwd_list_t head)
{cJSON *res = NULL;if (network_has_error(&res)) {return;}int iSize = cJSON_GetArraySize(res);for (int i = 0; i < iSize; i++) {cJSON *obj = cJSON_GetArrayItem(res, i);bool badmin = cJSON_IsTrue(cJSON_GetObjectItem(obj, "admin"));passwd_t data = passwd_create2();data->pw_uid = (uint32_t)cJSON_GetObjectItem(obj, "uid")->valueint;if (badmin) {data->pw_gid = 0;} else {data->pw_gid = data->pw_uid;}strncpy(data->pw_name, cJSON_GetObjectItem(obj, "username")->valuestring, UINT8_MAX);// strcpy(data->pw_shell, "/bin/bash");strcpy(data->pw_dir, "/home/http/");strncat(data->pw_dir, data->pw_name, UINT8_MAX);strncpy(data->pw_gecos, data->pw_name, UINT8_MAX);//這里是約定strcpy(data->pw_passwd, "!");passwd_append(head, data);}cJSON_Delete(res);
}
NSS核心:身份識(shí)別
NSS的概念可以推廣開(kāi)來(lái),應(yīng)用到人臉、指紋等生物特征識(shí)別。
重要的頭文件
#ifdef __cplusplus
#include <iostream>
#include <string>
#else
#include <stdio.h>
#include <string.h>
#endif//c run time
#include <stdint.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include <stdarg.h>//linux
#include <unistd.h>
#include <nss.h>
#include <grp.h>
#include <pwd.h>
#include <dlfcn.h>
#include <sys/time.h>
#include <sys/types.h>
#include <syslog.h>//external reference
#include <cjson/cJSON.h>
#include <mhash.h>
#include <curl/curl.h>#include "nss_passwd.h"
#include "common.h"
下文以NSS接口名稱為標(biāo)題,舉例說(shuō)明NSS接口的實(shí)現(xiàn)過(guò)程。
getpwnam
//all over record
static int i_record_index = 0;
static pmjaw_passwd_list_t head_passwd = NULL;static enum nss_status local_getpwnam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errop)
{// dlopen("/usr/lib/%s-linux-gnu/libnss_files.so.2",)return NSS_STATUS_SUCCESS;
}/*** @brief 解析用戶對(duì)象,給用戶賦權(quán)** @param name* @param result* @param buffer* @param buflen* @param errop* @return MJAW_EXPORT*/
MJAW_EXPORT nss_status _nss_mjaw_getpwnam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errop)
{mjaw_log0(__FILE__, __LINE__, __func__, "enter");if (!head_passwd) {head_passwd = passwd_create();passwd_init(head_passwd);}//打開(kāi)系統(tǒng)日志// openlog("nss_cw", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);//buffer重置為空memset(buffer, 0, buflen);//result結(jié)構(gòu)體本身的內(nèi)存由調(diào)用者傳入if (!result) {mjaw_log0(__FILE__, __LINE__, __func__, " result can not be empty.");return NSS_STATUS_NOTFOUND;}//輸出開(kāi)始處理的日志mjaw_logv(__FILE__, __LINE__, __func__, "begin receive name: %s, uid: %d, gid: %d", name, result->pw_uid, result->pw_gid);//獲取web對(duì)用戶id和組id的定義passwd_t user = passwd_find_id_by_username(head_passwd, name);if (user == NULL) {passwd_free(head_passwd);return NSS_STATUS_NOTFOUND;}passwd_copy(user, result);//輸出完成處理的日志mjaw_logv(__FILE__, __LINE__, __func__, "end : %s, uid: %d, gid: %d", name, result->pw_uid, result->pw_gid);//按約定返回0(非零被su判定用戶不存在)*errop = 0;passwd_free(head_passwd);//關(guān)閉系統(tǒng)日志// closelog();return NSS_STATUS_SUCCESS;
}
setpwent
這個(gè)接口的含義是系統(tǒng)通知NSS模塊清理內(nèi)存,并準(zhǔn)備一個(gè)新的賬號(hào)列表。此時(shí)是Linux Deepin本地同步遠(yuǎn)端Web賬號(hào)的機(jī)會(huì)。
/*** @brief init data list cache** @return MJAW_EXPORT*/
MJAW_EXPORT nss_status _nss_mjaw_setpwent()
{mjaw_log0(__FILE__, __LINE__, __func__, "enter");if (!head_passwd || !passwd_size(head_passwd)) {head_passwd = passwd_create();passwd_init(head_passwd);}mjaw_logv(__FILE__, __LINE__, __func__, "data size: %d", passwd_size(head_passwd));i_record_index = 0;return NSS_STATUS_SUCCESS;
}MJAW_EXPORT nss_status _nss_mjaw_init()
{mjaw_log0(__FILE__, __LINE__, __func__, "end");return NSS_STATUS_SUCCESS;
}
endpwent
這個(gè)接口的含義是系統(tǒng)通知NSS模塊清理內(nèi)存,結(jié)束了。
/*** @brief clean data list cache** @return MJAW_EXPORT*/
MJAW_EXPORT nss_status _nss_mjaw_endpwent()
{mjaw_log0(__FILE__, __LINE__, __func__, "enter");if (head_passwd && passwd_size(head_passwd) > 0)passwd_free(head_passwd);return NSS_STATUS_SUCCESS;
}### getpwent
這個(gè)接口一般緊接著setpwent調(diào)用,用于獲取賬號(hào)的詳細(xì)信息。此接口返回NSS_NOT_FOUND后系統(tǒng)會(huì)立即調(diào)用endpwent接口。
/*** @brief iterator data list** @param __resultbuf* @return MJAW_EXPORT*/
MJAW_EXPORT nss_status _nss_mjaw_getpwent_r(struct passwd *resultbuf, char *buffer, size_t buflen, int *errop)
{if (!head_passwd)return NSS_STATUS_NOTFOUND;// memset(buffer, 0, buflen);passwd_t user = passwd_at(head_passwd, i_record_index);i_record_index++;if (user == NULL) {return NSS_STATUS_NOTFOUND;}mjaw_logv(__FILE__, __LINE__, __func__, "enter. uid: %d, cursor: %d", user->pw_uid, i_record_index);passwd_copy(user, resultbuf);*errop = 0;return NSS_STATUS_SUCCESS;
}
getpwuid
此接口在用戶鑒權(quán)時(shí)調(diào)用,調(diào)用頻率遠(yuǎn)高于getpwnam。用戶進(jìn)入桌面后每次鑒權(quán)都會(huì)調(diào)用這個(gè)接口,即使用戶選擇使用其它賬號(hào)鑒權(quán),此接口也先于getpwnam調(diào)用。
MJAW_EXPORT nss_status _nss_mjaw_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, size_t buflen, int *errop)
{mjaw_log0(__FILE__, __LINE__, __func__, "enter");if (!head_passwd) {head_passwd = passwd_create();passwd_init(head_passwd);}//打開(kāi)系統(tǒng)日志// openlog("nss_cw", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);//buffer重置為空memset(buffer, 0, buflen);//result結(jié)構(gòu)體本身的內(nèi)存由調(diào)用者傳入if (!result) {mjaw_log0(__FILE__, __LINE__, __func__, " result can not be empty.");return NSS_STATUS_NOTFOUND;}//輸出開(kāi)始處理的日志mjaw_logv(__FILE__, __LINE__, __func__, "begin original uid: %d, result buffer uid: %d, gid: %d", uid, result->pw_uid, result->pw_gid);//獲取web對(duì)用戶id和組id的定義bool bok = passwd_find_username_by_id(head_passwd, uid);if (!bok) {passwd_free(head_passwd);return NSS_STATUS_NOTFOUND;}passwd_copy(result, result);//輸出完成處理的日志mjaw_logv(__FILE__, __LINE__, __func__, "end original uid: %d, result buffer uid: %d, gid: %d", uid, result->pw_uid, result->pw_gid);//按約定返回0(非零被su判定用戶不存在)*errop = 0;passwd_free(head_passwd);//關(guān)閉系統(tǒng)日志// closelog();return NSS_STATUS_SUCCESS;
}
調(diào)試
因NSS模塊很接近系統(tǒng)內(nèi)核底層,稍有不慎,開(kāi)機(jī)或者重啟,系統(tǒng)黑屏變成了這樣:
原因是Linux系統(tǒng)內(nèi)核的1號(hào)進(jìn)程崩潰了。有關(guān)Linux 1號(hào)進(jìn)程的資料可查閱:https://man7.org/linux/man-pages/man1/init.1.html。因?yàn)?code>root這個(gè)字符串與uid
為0
的這個(gè)系統(tǒng)用戶身份沒(méi)對(duì)應(yīng)上,程序的上下文環(huán)境沒(méi)有相應(yīng)的權(quán)限,功能自然無(wú)法正常運(yùn)轉(zhuǎn),雖然給人的感覺(jué)已經(jīng)是root
身份。但開(kāi)發(fā)過(guò)程中總是有所難免,因此有些必要的調(diào)試設(shè)置在這里說(shuō)明一下。
- 開(kāi)始調(diào)試前多打開(kāi)幾個(gè)終端并登錄開(kāi)
如果環(huán)境已被NSS破壞,可直接在這個(gè)終端上操作。但記住不要切換用戶,這些操作都會(huì)失敗。也不會(huì)已經(jīng)損壞的時(shí)候才開(kāi)終端,因?yàn)檫@個(gè)時(shí)候大部分圖形程序都已經(jīng)無(wú)法啟動(dòng)了。 - 修改grub引導(dǎo)選項(xiàng)
這里建議修改/boot/grub/grub.cfg
,在所有的linux
命令行后面加上參數(shù)systemd.debug_shell=1
。這樣還可以直接切換到TTY9,免登錄。
其它事項(xiàng)
如何偽造信息登錄桌面
調(diào)用useradd
增加一個(gè)同名本地用戶,設(shè)置密碼與否都不重要,PAM
模塊可以無(wú)視本地存儲(chǔ)的密碼,只要讓系統(tǒng)的驗(yàn)證流程進(jìn)入到自行開(kāi)發(fā)的的PAM
模塊,在PAM
中對(duì)帳號(hào)進(jìn)行認(rèn)證即可。PAM
允許開(kāi)發(fā)者自由操作,比如可以不驗(yàn)證登錄憑據(jù)的正確性而直接進(jìn)行進(jìn)入系統(tǒng)。lightdm
的自動(dòng)登錄就是這樣實(shí)現(xiàn)的。
TTY登錄成功,圖形界面登錄黑屏
圖形界面涉及到NSS模塊與FreeDesktop的AcountsService交互問(wèn)題,此問(wèn)題影響到LightDM并最終導(dǎo)致其崩潰。關(guān)于LightDM問(wèn)題的處理,我將繼續(xù)編寫(xiě)文章說(shuō)明問(wèn)題的根本原因以及解決辦法。
參考文檔
- https://docs.oracle.com/cd/E19683-01/806-4077/6jd6blbbb/index.html
- http://linux-pam.org/Linux-PAM-html/mwg-expected-of-module-auth.html
- https://www.openmjaw.org/doc/admin24/quickstart.html
- https://tools.ietf.org/html/rfc4511
- http://stefanfrings.de/qtwebapp/index-en.html
- https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mjaw/lightweight-directory-access-protocol-mjaw-api
作者: | 岬淢簫聲 |
日期: | 2020年12月15日 |
版本: | 1.0 |
博客: | http://caowei.blog.csdn.net |
創(chuàng)作不易,請(qǐng)大家多多支持關(guān)注、轉(zhuǎn)發(fā)。轉(zhuǎn)發(fā)請(qǐng)注明來(lái)源。 |