a474f1d0077c19e26e06742b402a4262737a7547
[citadel.git] / citadel / utils / auth.c
1 // system-level password checking for host auth mode
2 //
3 // Copyright (c) 1999-2022 by Nathan Bryant, Trey Van Riper, and the citadel.org team
4 //
5 // This program is open source software.  Use, duplication, or disclosure
6 // is subject to the terms of the GNU General Public License, version 3.
7 // The program is distributed without any warranty, expressed or implied.
8
9 #if defined(__linux) || defined(__sun)  // needed for crypt():
10 #define _XOPEN_SOURCE
11 #define _XOPEN_SOURCE_EXTENDED 1
12 #endif
13
14 #include <pwd.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <crypt.h>
20
21 #include "auth.h"
22 #include "../server/sysdep.h"
23
24 #ifdef HAVE_GETSPNAM
25 #include <shadow.h>
26 #endif
27
28 #ifdef HAVE_PAM_START
29 #include <security/pam_appl.h>
30
31 // struct appdata: passed to the conversation function
32 struct appdata {
33         const char *name;
34         const char *pw;
35 };
36
37 // conv(): the PAM conversation function. this assumes that a
38 // PAM_PROMPT_ECHO_ON is asking for a username, and a PAM_PROMPT_ECHO_OFF is
39 // asking for a password. esoteric authentication modules will fail with this
40 // code, but we can't really support them with the existing client protocol
41 // anyway. the failure mode should be to deny access, in any case.
42 static int conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {
43         struct pam_response *temp_resp;
44         struct appdata *data = appdata_ptr;
45
46         if ((temp_resp =
47              malloc(sizeof(struct pam_response[num_msg]))) == NULL)
48                 return PAM_CONV_ERR;
49
50         while (num_msg--) {
51                 switch ((*msg)[num_msg].msg_style) {
52                 case PAM_PROMPT_ECHO_ON:
53                         temp_resp[num_msg].resp = strdup(data->name);
54                         break;
55                 case PAM_PROMPT_ECHO_OFF:
56                         temp_resp[num_msg].resp = strdup(data->pw);
57                         break;
58                 default:
59                         temp_resp[num_msg].resp = NULL;
60                 }
61                 temp_resp[num_msg].resp_retcode = 0;
62         }
63
64         *resp = temp_resp;
65         return PAM_SUCCESS;
66 }
67 #endif                          // HAVE_PAM_START
68
69
70 // check that `pass' is the correct password for `uid'
71 // returns zero if no, nonzero if yes
72 int validate_password(uid_t uid, const char *pass) {
73         if (pass == NULL) {
74                 return (0);
75         }
76 #ifdef HAVE_PAM_START
77         struct pam_conv pc;
78         struct appdata data;
79         pam_handle_t *ph;
80         int i;
81 #else
82         char *crypted_pwd;
83 #ifdef HAVE_GETSPNAM
84         struct spwd *sp;
85 #endif
86 #endif
87         struct passwd *pw;
88         int retval = 0;
89
90         pw = getpwuid(uid);
91         if (pw == NULL) {
92                 return retval;
93         }
94 #ifdef HAVE_PAM_START
95
96 #ifdef PAM_DATA_SILENT
97         int flags = PAM_DATA_SILENT;
98 #else
99         int flags = 0;
100 #endif
101
102         pc.conv = conv;
103         pc.appdata_ptr = &data;
104         data.name = pw->pw_name;
105         data.pw = pass;
106         if (pam_start("citadel", pw->pw_name, &pc, &ph) != PAM_SUCCESS)
107                 return (0);
108
109         if ((i = pam_authenticate(ph, flags)) == PAM_SUCCESS) {
110                 if ((i = pam_acct_mgmt(ph, flags)) == PAM_SUCCESS) {
111                         retval = -1;
112                 }
113         }
114
115         pam_end(ph, i | flags);
116 #else
117         crypted_pwd = pw->pw_passwd;
118
119 #ifdef HAVE_GETSPNAM
120         if (pw == NULL)
121                 return (0);
122         if (pw->pw_name == NULL)
123                 return (0);
124         if ((sp = getspnam(pw->pw_name)) != NULL) {
125                 crypted_pwd = sp->sp_pwdp;
126         }
127 #endif
128
129         if (!strcmp(crypt(pass, crypted_pwd), crypted_pwd)) {
130                 retval = -1;
131         }
132 #endif                          // HAVE_PAM_START
133
134         return retval;
135 }