LDAP/SASL/GSSAPI/Kerberos programming API(4)--krb5 client (continued 1--local)

The previous article <Programming API (2)--krb5 client> introduced the local application example of krb5_get_init_creds_password() function input user and password authenticated by krb5 server,'client' means that the authentication is as a krb5 client, and its application It is still local, and there is no server side of the application. For example, Unix local login PAM plug-in libpam-krb5
This article is also a local application, but assuming that there is a ticket, it can be verified without entering the user and password.

Such as: su command

linlin@debian:~$ klist
Credentials cache: FILE:/tmp/krb5cc_1000
Principal: [email protected]

Issued Expires Principal
Jul 27 07:24:53 2020 Jan 25 22:24:48 2021 krbtgt/[email protected]
linlin@debian:~$
linlin@debian:~$ su krblinlin
Password: Enter the krb5 principal user krblinlin Password
krblinlin@debian:/home/linlin$

I expected su to no longer enter the password, but after testing, the krb5-ized su still prompts to enter the password when there is a ticket. I wonder if it is a configuration problem? Or I misunderstood the krb5 usage of su? In
libpam-krb5 Obviously there is a krb5_kuserok() function. This article refers to the libpam-krb5 and MIT krb5 source code package test cases/src/krb5/1.18.3-4/src/tests/localauth.c

The MIT Kerberos document explains krb5_kuserok:
Determine if a principal is authorized to log in as a local user.
Does this sentence in English indicate that krb5_kuserok is used to verify whether the principal user is a local user? Not as an authentication?

1. Source code
// Source file name: testlocalkrb.c

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

int main()
{ 
    krb5_context context = NULL;
    krb5_error_code krberr;
    krb5_principal kprincpw = NULL;

    const char * errmsg;
    krberr = krb5_init_context(&context);

    if (krberr) {
            errmsg = krb5_get_error_message(NULL, krberr);
            printf("Err: Kerberos context initialization failed -> %s\n", errmsg);
            goto cleanup;
        }

    krberr = krb5_parse_name(context, "[email protected]", &kprincpw);//#1
/*
    //#3
    krb5_ccache ccdef;
    krberr = krb5_cc_default(context, &ccdef);
    krberr = krb5_cc_get_principal(context, ccdef, &kprincpw );
*/   
    if (krberr) {
            errmsg = krb5_get_error_message(context, krberr);
            printf("Err: Failed to parse princpal %s -> %s\n", errmsg);
            goto cleanup;
        }

    // krb5_kuserok返回的是krb5_boolean  
    if (!krb5_kuserok(context , kprincpw , "krblinlin")) //#2
    { 
        printf("Err: Failed\n " );
        goto cleanup;
    }
    printf("krb5_kuserok\n " );

cleanup: 
    if (kprincpw) krb5_free_principal(context, kprincpw);
    if (context) krb5_free_context(context);

    return 0;   
}

2. Parse the
MIT Kerberos document
krb5_boolean krb5_kuserok(krb5_context context, krb5_principal principal, const char * luser)

param:

[in] context - Library context

[in] principal - Principal name

[in] luser - Local username

3.编译
linlin@debian:~$ gcc -o testlocalkrb testlocalkrb.c -lkrb5

4.配置
linlin@debian:~$ cat /etc/krb5.conf
[libdefaults]
default_realm = CTP.NET
linlin@debian:~$

5. Add krblinlin local user The
local user krblinlin password can be different from the main user of krblinlin
linlin@debian:~$ cat /etc/passwd |grep linlin
linlin:x:1000:1000:,,,:/home/linlin:/bin /bash
krblinlin:x:1001:1001:,,,:/home/krblinlin:/bin/bash
linlin@debian:~$

6. Run
first to
generate a ticket linlin@debian:~$kinit --no-forwardable krblinlin
[email protected]'s Password:
linlin@debian:~$ klist
Credentials cache: FILE:/tmp/krb5cc_1000
Principal: [email protected]
. ..
linlin@debian:~$

Run testlocalkrb program has been verified successfully
linlin@debian:~$ ./testlocalkrb
krb5_kuserok
linlin@debian:~$

linlin@debian:~$ strace ./testlocalkrb
...
openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
lseek(3, 0,
SEEK_CUR ) = 0 fstat(3, {st_mode=S_IFREG|0644 , st_size=1869, ...}) = 0
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 1869
close(3) = 0
access("/home/krblinlin/.k5login", F_OK) = -1 ENOENT (there is no such file or directory)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x4), ...} ) = 0
write(1, "krb5_kuserok\n", 13krb5_kuserok) = 13
...
linlin@debian:~$

It can be seen that through the krb5_kuserok() function, the local application does not need to enter a password when a ticket already exists

7. Follow up and debug the
order of debugging steps
1) No ticket
linlin@debian:~$ klist
klist: No ticket file: /tmp/krb5cc_1000
linlin@debian:~$ ./testlocalkrb
Err: Failed
linlin@debian:~$
operation failed

2) The following debugging first generates a ticket
linlin@debian:~$ klist
Credentials cache: FILE:/tmp/krb5cc_1000
Principal: [email protected]
...
linlin@debian:~$

3) There is no krblinlin local user to
run the testlocalkrb program failed

linlin@debian:~$ strace ./testlocalkrb
...
openat(AT_FDCWD, "/etc/krb5.conf", O_RDONLY) = 3
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=133, ...}) = 0
read(3, "[libdefaults]\n\n\tdefault_realm = "..., 4096) = 133
...
openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
lseek(3, 0, SEEK_CUR) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=1817, ...}) = 0
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 1817
lseek(3, 0, SEEK_CUR) = 1817
read(3, "", 4096) = 0
close(3) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x4), ...}) = 0
write(1, "Err: Failed\n", 12Err: Failed) = 12
...
linlin@debian:~$
tracked by strace, it is krb5_kuserok that needs to read the /etc/passwd file, and there is no krblinlin user locally and it fails, indicating that krb5_kuserok must have a local unix user

linlin@debian:~$ cat /etc/passwd |grep linlin
linlin:x:1000:1000:,,,:/home/linlin:/bin/bash
linlin@debian:~$

4) Add krblinlin local user to
run testlocalkrb program has been verified successfully

5) Turn off the kdc server
and run testlocalkrb on the client to verify success

6) Source code #2 "krblinlin" was changed to "linlin", and the
testlocalkrb program failed to recompile and run
linlin@debian:~$ strace ./testlocalkrb
...
openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
lseek(3, 0,
SEEK_CUR ) = 0 fstat(3, {st_mode=S_IFREG|0644, st_size=1869, ...}) = 0
read(3, "root:x:0:0:root:/ root:/bin/bash\n"..., 4096) = 1869
close(3) = 0
access("/home/linlin/.k5login", F_OK) = -1 ENOENT (there is no such file or directory)
fstat( 1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x4), ...}) = 0
write(1, "Err: Failed\n", 12Err: Failed) = 12
...
linlin@debian: ~$
Is it verified that the krb5 principal name and the unix user name mapping must match? The default principal name and the unix user name are the same as the same matching user?

7) Source code #2 "krblinlin" changed to NULL to
run a segmentation error

8) Source code #2 changed back to "krblinlin"

9) authentication ticket expiration
client and server disconnect kdc
date client
linlin @ debian: ~ $ date
2020 on Tuesday, December 15 UTC 06:31:53
linlin Debian @: ~ $ klist
Credentials Cache: FILE: /tmp/krb5cc_1000
Principal: [email protected]

Issued Expires Principal
Jul 17 01:12:47 2020 Jan 15 16:12:44 2021 krbtgt/[email protected]
linlin@debian:~$ ./testlocalkrb
krb5_kuserok
linlin@debian:~$
shows that the ticket is valid until 2021 year

Modify the client date to 2022 to expire the ticket
linlin@debian:~$ date
Tuesday, February 15, 2022 06:34:39 UTC
linlin@debian:~$ klist
Credentials cache: FILE:/tmp/krb5cc_1000
Principal: krblinlin @CTP.NET

Issued Expires Principal
Jul 17 01:12:47 2020 >>>Expired<<< krbtgt/[email protected]

linlin@debian:~$ ./testlocalkrb
krb5_kuserok
linlin@debian:~$

It can be seen that the ticket has expired and the verification can still be successful

Change code #1 to #3, it still expires and can be verified successfully

10) Change back to code #1

11) When /etc/krb5.conf does not exist, or default_realm is not configured, the
testlocalkrb program fails to run
linlin@debian:~$ strace ./testlocalkrb
...
stat("/etc/krb5.conf", 0x7fff510fcbb0) = -1 ENOENT (There is no such file or directory) When krb5.conf does not exist
...
openat(AT_FDCWD, "/tmp/krb5cc_1000", O_RDONLY|O_CLOEXEC) = 3
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
fcntl(3, F_OFD_SETLKW, { l_type=F_RDLCK, l_whence=SEEK_SET, l_start=0, l_len=0)) = 0
fcntl(3, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE)
fstat(3, (st_mode=S_IFREG|0600, st_size=807, .. .}) = 0
read(3, "\5\4\0\0\0\0\0\1\0\0\0\1\0\0\0\7CTP.NET\0\0\0 \tkrbli"..., 4096) = 807
...
openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
lseek(3, 0, SEEK_CUR) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=1869, ...}) = 0
read(3, "root:x:0:0:root:/root:/bin/bash\n"... , 4096) = 1869
close(3) = 0
access("/home/krblinlin/.k5login", F_OK) = -1 ENOENT (there is no such file or directory)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev (0x88, 0x4), ...}) = 0
write(1, "Err: Failed\n", 12Err: Failed) = 12
...
linlin@debian:~$

After testing, it is normal to configure default_realm = CTP.NET. Can you set default_realm in the program?

12) su command test
root@debian:/# apt-get install libpam-krb5
is reading the package list...Done

The root user su to the ordinary user does not need to enter a password, so the following test su command is performed under the ordinary user linlin
linlin@debian:~$ klist
klist: No ticket file: /tmp/krb5cc_1000
linlin@debian:~$
display No bills

Switch to krblinlin user
linlin@debian:~$ su krblinlin
Password: Enter the principal user password
krblinlin@debian:/home/linlin$ klist
Credentials cache: FILE:/tmp/krb5cc_1001_s3gn7n
Principal: [email protected]

Issued Expires Principal
Jul 27 07:39:09 2020 Jul 28 07:39:09 2020 krbtgt/[email protected]
krblinlin@debian:/home/linlin$
displays su after generating a ticket, pay attention to the note in the file name '1001', obviously the user id of krblinlin, not the user id of linlin (1000)

Exit from krblinlin and return to linlin
krblinlin@debian:/home/linlin$ exit
exit
linlin@debian:~$ ls /tmp
linlin@debian:~$ The
krb5cc_1001_s3gn7n ticket is gone under /tmp, it should be libpam-krb5 destroy the ticket after exiting


Generate a ticket first and then su linlin@debian:~$ kinit --no-forwardable krblinlin
[email protected]'s Password:
linlin@debian:~$ klist
Credentials cache: FILE:/tmp/krb5cc_1000
Principal: [email protected]
.. .
linlin@debian:~$
linlin@debian:~$ su krblinlin
Password: mistype
su: identification failure
linlin@debian:~$ su krblinlin
Password: enter the krb5 main user's password
krblinlin@debian:/home/linlin$
has been entered krblinlin

The above can only show that su still has to prompt for a password under an existing ticket. I don't know whether libpam-krb5 can be configured to avoid input password. The
libpam-krb5 source code has created a ticket such as krb5cc_pam_xx, but the above su experiment cannot see that the
following LXC container ( Name: vmcln) Test su, use LXC resources to control the low occupancy rate of the container cpu, observe the file change of the directory/tmp where the ticket is located
, view the container vmcln, the default cpu occupancy ratio is 1024
root@debian:/home/linlin# lxc-cgroup -n vmcln cpu.shares
1024
root@debian:/home/linlin#


Go to the container su linlin@vmcln:~$ su krblinlin
Password: Enter the password, don’t press Enter for now

To the host, set the container cpu to be very low, set to 2
root@debian:/home/linlin# lxc-cgroup -n vmcln cpu.shares 2
root@debian:/home/linlin#

Execute infinite loop on the host
linlin@debian:~$ while true; do true; done

The cpu.shares on the container vmcln obviously works, and the container is running very slowly

Execute the infinite loop that lists the temporary directory of the container on the host, and then press Enter when you go to the container. After a few seconds, see the output process
linlin@debian:~$ while true; do ls -l /mnt/vm/lxc/vmcln/ tmp |grep krb5; done
-rw------- 1 root linlin 0 December 3 14:39 krb5cc_pam_lU0B9q
...
-rw------- 1 root linlin 48 December 3 14:39 krb5cc_pam_lU0B9q
. ..48 and 506 are file size bytes
-rw------- 1 root linlin 506 December 3 14:39 krb5cc_pam_lU0B9q
...
-rw------- 1 root 1001 0 December 3 14 : 39 krb5cc_1001_xjZF0q
...
-rw -------. 1 1001 48 12 is the root dated krb5cc_1001_xjZF0q 14:39. 3
...
-rw -------. 1 dated 12 is the root 506 Linlin krb5cc_pam_lU0B9q 14:39. 3
- rw------- 1 root 1001 506 December 3 14:39 krb5cc_1001_xjZF0q
... The user owner of the bill krb5cc_1001_xjZF0q is changed from root to krblinlin (user id=1001)
-rw------- 1 1001 1001 506 December 3 14:39 krb5cc_1001_xjZF0q
-rw------- 1 root linlin 506 December 3 14:39 krb5cc_pam_lU0B9q
-rw------- 1 1001 1001 506 December 3 14:39 krb5cc_1001_xjZF0q
...The bill krb5cc_pam_lU0B9q disappeared
-rw------- 1 1001 1001 506 December 3 14:39 krb5cc_1001_xjZF0q

The command su has the setuid bit. It can be seen from the above that after the su krb5 verification is passed, the krb5cc_pam_xx bill owned by the root user is created, and then the krb5cc_1001_xx bill owned by the krblinlin (user id=1001) imported by su is created, and then the krb5cc_pam_xx bill is destroyed. Therefore, su The last ticket is in the session of the user after the switch

I still haven't tracked the krb5_kuserok() function, so I won't explore it anymore.

7.Summary
1) The usual application that uses krb5 authentication is a typical C/S structure with an application server. When the client already has a ticket, there is no need to enter the user and password.
2) The local application that uses krb5 authentication is currently only See Unix local login. At present, when a ticket exists, the su command still has to enter a password.
3) Although this article has verified that the krb5_kuserok() function can be used in local applications, there is no need to enter a password when a ticket exists, but I am not sure about the krb5_kuserok() function In which aspect of the application, it seems to be related to unix users
4) The expiration of the ticket does not work for krb5_kuserok

Guess you like

Origin blog.51cto.com/13752418/2573750