在 C 中模仿 Linux adduser 命令
有趣的编程也可以体现在悠闲的编码实践中。不管学术上的不足,这项任务确实有助于程序员理解语言的足智多谋。在这里,让我们看看一个独特的编程活动,它也转化为低级洞察力和流利度建设练习。
问题陈述:模仿 Linux 上的 adduser 命令,以使用 C 程序作为编程语言创建普通用户或系统用户。 这是问题隐含地要求作为解决方案的内容,以便程序员遵守命令的正式执行:
- 在执行期间,必须在 Linux 终端上提供 CLI 的精确复制。
- 必须处理passwd文件。
- 还必须处理影子文件。
- 必须通过指定PFILE环境变量来管理这两个文件的执行。
先决条件:在开始处理代码之前,让我们快速浏览一下先决条件。首先,从掌握passwd文件背后的想法开始。另外,请随时参考 Linux 手册页。
1. passwd 文件:技术上表示为 /etc/passwd 文件,该文件的主要目的是监视或存储已被授予系统访问权限的注册用户的记录。它具有7个重要领域的记录。
UID 本质上是用户 ID,GID 是组 ID。同时,GECOS是用于存储用户真实姓名和电话号码的字段。 Login Shell 是负责读取文件和设置环境变量的字段。
这些字段中的每一个都用冒号分隔。出于该程序的目的,详细信息将一直存储到 GECOS 字段。尽管如此,我们鼓励读者进一步探索以纳入其他领域。
2、shadow文件:正式定义为/etc/shadow文件,这个文件的作用类似于passwd文件。但是,它侧重于与 passwd 文件中的加密密码相关的所有内容。与 passwd 文件不同,它只能由 root 用户读取。通常,它具有 9 个字段的记录。
密码必须保持其加密格式——这是一个有趣的地方,我们将开发我们自己的加密算法(基本算法)。第三个和第四个字段是指密码更改之间的最小和最大间隔天数(默认为 0)。第 5 个和第 6 个字段定义了用户必须在密码到期和禁用帐户通知前多长时间进行提示。所有这些字段都是完全可定制的。请记住对进入此文件的所有数据进行加密。
3. PFILE ENV:环境变量的目的是影响程序或软件的其他行为方式。例如,$LANG 变量会向软件传达用户理解特定语言的信息,因此它必须在界面级别提供相同的语言。在这里,我们将使用 PATH 变量来模拟系统查找 passwd 和 shadow 文件的位置。我们将为其分配名称 PFILE。
命令选项:在终端上输入 useradd 时,通常会指定另一个称为“选项”的值。选项可以是各种字母。但是,要限制此程序,请仅使用-r和-m选项。
- -r 选项创建系统用户。
- -m 选项将主目录与用户帐户相关联。
句法:
笔记:
- useradd 是 7 个字符长。
- useradd 后跟一个空格,然后是扩展到下一个空格字符的用户名。
- 在第二个空格之后,字符是连字符和选项字符。
- 输入命令后,密码就设置好了。
以下是使用C程序实现它的步骤:
1. main.c:这里指定所有的头文件和变量,并给出所有的函数调用。它应作为编译器的蓝图。
在这里,定义一个结构来保存所有必须更新的数据。任何其他方法都会引入额外的代码行并使跟踪代码变得困难。此外,将始终重命名您创建的结构作为很好的建议。当使用 struct 作为参数和返回类型时,不重命名将在编译期间导致大量引用和标识符错误。
int main() 是调用所有函数和加载过程的基础。 void files_and_env()是将 PATH 变量分配给PFILE并初始化文件的地方。请记住插入为 f1 和 f2 创建的文件的有效位置或路径。继续, Struct chk_getU_op()是一个函数,它检查终端上的输入命令,从命令和选项中获取用户名。它返回一个struct类型的对象。
注意:在 Struct 中使用大写 S(在声明返回类型时),因为 typedef 用于重命名第 12 行中的结构。最后,void setpass()提示用户将密码与已创建的新用户相关联。这个函数是参数化的,即,它接收之前从chk_getU_op()返回的对象。
下面是实现上述方法的 C 程序:
C
// C program to implement
// the above approach
#include "server.h"
#include
#include
#include
#include
struct name_opt {
char username[32];
char opt;
char password[100];
};
typedef struct name_opt Struct;
// Explicit declaration
void files_and_env(void);
void setpass(Struct);
Struct chk_getU_op(void);
// Driver code
int main()
{
Struct fn;
// Additional character denoting
// the option
char opt;
// Function to declare files and
// set environment variables
files_and_env();
fn = chk_getU_op();
setpass(fn);
return (0);
}
void files_and_env()
{
FILE *f1, *f2;
// Assigning environment variable
// PFILE for protecting and
// restricting access
putenv(
"PFILE = -/Environment/rms");
// Displays the path of the
// environment variable
printf("PFILE: %s\n",
getenv("PFILE"));
f1 = fopen("Location of file\\passwd.txt", "r");
f2 = fopen("Location of file\\shadow.txt", "r");
}
Struct chk_getU_op()
{
int i = 8;
int ctr = 0;
int x, l = 0;
char command[] = { 'u', 's', 'e', 'r',
'a', 'd', 'd' };
// Linux accepts only 42 char
// usernames
char FunctionCall[42];
Struct send;
gets(FunctionCall);
for (x = 0; x < 7; x++) {
if (FunctionCall[x] == command[x]) {
ctr += 1;
}
}
if (ctr != 7) {
printf("Invalid command.\n");
exit(0);
}
else {
// Ends when space char is reached
while ((int)FunctionCall[i] != 32) {
send.username[i - 8] = FunctionCall[i];
i++;
}
l = strlen(send.username);
send.opt = FunctionCall[10 + l];
}
return send;
}
void setpass(Struct F)
{
char pass[100];
char UID[4];
printf("Changing password for user.....\n");
printf("New UNIX password: ");
scanf("%s\n", *(F.password));
if (F.opt == 'r') {
printf("UID: ");
scanf("%s", UID);
if (strlen(UID) == 2) {
// Calling password function
passwd(F.username, F.password,
UID);
// Calling shadow function
shadow(F.username, F.password,
UID);
}
// When the UID is not 2 characters long
else {
while (strlen(UID) != 2) {
printf("Invalid UID. Retype UID: ");
scanf("%s", UID);
}
// Calling password function
passwd(F.username, F.password,
UID);
// Calling shadow function
shadow(F.username, F.password,
UID);
}
}
// If the option is 'm'
else {
printf("UID: ");
scanf("%s", UID);
if (strlen(UID) <= 2) {
printf("Normal Users not allowed this UID!\n");
printf("Retype UID: ");
scanf("%s", UID);
passwd(F.username, F.password, UID);
shadow(F.username, F.password, UID);
}
else {
passwd(F.username, F.password, UID);
shadow(F.username, F.password, UID);
}
// end of setpass
}
// end of main.c
}
C
// C program to implement
// the above approach
#include "server.h"
#include
#include
#include
void passwd(char* username,
char* password,
char* UID)
{
int len, k = 0;
// Pointing to the password file
// that will now be appended
FILE* fs;
fs = fopen("Location of the file\\passwd.txt",
"+a");
len = strlen(password);
char temp_passwrd[len];
for (k = 0; k < len; k++) {
// Since the passwd may not
// reveal user-sensitive info
temp_passwrd[k] = '$';
}
fprintf(fs, "%s:%c:%s:%s\n", username,
'x', temp_passwrd, UID);
fclose(fs);
// end of passwd.c
}
C
// C program to implement
// the above approach
#include "server.h"
#include
#include
#include
#include
#include
#include
void shadow(char* username,
char* password,
char* UID)
{
char epass;
// File pointer
FILE* fp1;
fp1 = fopen("Location of file\\shadow.txt",
"+a");
// Call the encryption function
Encryption(epass, fp1, username, UID);
}
void Encryption(char* epass, FILE* fp1,
char* username, char* UID)
{
// Integer that will help in indexing
int x = 0;
char* e = epass;
char encrypt[100];
// While is prefered over for loops here
while (*e) {
if (islower(*e) == true) {
if (*e == 'a') {
// a becomes !
encrypt[x] = '!';
}
else if (*e == 'z') {
// z becomes %
encrypt[x] = '%';
}
else {
// Make capital
encrypt[x] = toupper((char)((int)*e) + 1);
}
}
// If we have upper case letters
else {
// Increment and make lower
encrypt[x] = tolower((char)(((int)*e) + 1));
}
e++;
x++;
// end of while
}
// end of shadow.c
}
2. passwd.c 文件:这里存储了刚刚创建的用户名的详细信息。数据写入 passwd.txt 文件。使用函数声明的另一个数组来屏蔽之前在 setpass()函数输入的密码内容。
下面是实现上述方法的 C 程序:
C
// C program to implement
// the above approach
#include "server.h"
#include
#include
#include
void passwd(char* username,
char* password,
char* UID)
{
int len, k = 0;
// Pointing to the password file
// that will now be appended
FILE* fs;
fs = fopen("Location of the file\\passwd.txt",
"+a");
len = strlen(password);
char temp_passwrd[len];
for (k = 0; k < len; k++) {
// Since the passwd may not
// reveal user-sensitive info
temp_passwrd[k] = '$';
}
fprintf(fs, "%s:%c:%s:%s\n", username,
'x', temp_passwrd, UID);
fclose(fs);
// end of passwd.c
}
passwd.txt 文件在执行后必须看起来像这样:
3. shadow.c 文件:如前所述,该文件的主要函数是对从终端输入的密码进行加密。读者必须尝试不同的加密技术,而不是简单的字母数字方案。但是,开发一种非常基本的技术(如下所示)是一个好的开始。
下面是实现上述方法的 C 程序:
C
// C program to implement
// the above approach
#include "server.h"
#include
#include
#include
#include
#include
#include
void shadow(char* username,
char* password,
char* UID)
{
char epass;
// File pointer
FILE* fp1;
fp1 = fopen("Location of file\\shadow.txt",
"+a");
// Call the encryption function
Encryption(epass, fp1, username, UID);
}
void Encryption(char* epass, FILE* fp1,
char* username, char* UID)
{
// Integer that will help in indexing
int x = 0;
char* e = epass;
char encrypt[100];
// While is prefered over for loops here
while (*e) {
if (islower(*e) == true) {
if (*e == 'a') {
// a becomes !
encrypt[x] = '!';
}
else if (*e == 'z') {
// z becomes %
encrypt[x] = '%';
}
else {
// Make capital
encrypt[x] = toupper((char)((int)*e) + 1);
}
}
// If we have upper case letters
else {
// Increment and make lower
encrypt[x] = tolower((char)(((int)*e) + 1));
}
e++;
x++;
// end of while
}
// end of shadow.c
}
shadow.txt文件中的更改将如下所示:
请注意所有三个源代码文件中的server.h标头。这是一个引用 passwd.c 和 shadow.c 文件中所有函数的文件。包含这样一个文件的原因是 main.c 文件会随时了解 passwd、shadow 和 Encryption 文件所在的位置。这是 server.h 文件包含的内容:
确保顺序执行和适当编译多个源代码文件的一个好做法是使用 makefile。 Makefile 有助于保持所有源代码文件在执行时编译,以便在编辑任何其他源代码时,其他文件仍然可以执行。鼓励读者养成这种习惯。