1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
26 */
27
28 #include <sys/types.h>
29 #include <sys/varargs.h>
30 #include <string.h>
31 #include <syslog.h>
32 #include <stdlib.h>
33
34 #include <security/pam_appl.h>
35 #include <security/pam_modules.h>
36 #include <security/pam_impl.h>
37
38 #include <libintl.h>
39 #include <passwdutil.h>
40
41 #include <smbsrv/libsmb.h>
42
43 /*PRINTFLIKE3*/
44 static void
error(boolean_t nowarn,pam_handle_t * pamh,char * fmt,...)45 error(boolean_t nowarn, pam_handle_t *pamh, char *fmt, ...)
46 {
47 va_list ap;
48 char message[PAM_MAX_MSG_SIZE];
49
50 if (nowarn)
51 return;
52
53 va_start(ap, fmt);
54 (void) vsnprintf(message, sizeof (message), fmt, ap);
55 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, &message,
56 NULL);
57 va_end(ap);
58 }
59
60 /*PRINTFLIKE3*/
61 static void
info(boolean_t nowarn,pam_handle_t * pamh,char * fmt,...)62 info(boolean_t nowarn, pam_handle_t *pamh, char *fmt, ...)
63 {
64 va_list ap;
65 char message[PAM_MAX_MSG_SIZE];
66
67 if (nowarn)
68 return;
69
70 va_start(ap, fmt);
71 (void) vsnprintf(message, sizeof (message), fmt, ap);
72 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, &message,
73 NULL);
74 va_end(ap);
75 }
76
77 int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)78 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
79 {
80 boolean_t debug = B_FALSE;
81 boolean_t nowarn = B_FALSE;
82 pwu_repository_t files_rep;
83 const char *user;
84 char *local_user;
85 const char *newpw;
86 const char *service;
87 int privileged;
88 int res;
89 int i;
90
91 for (i = 0; i < argc; i++) {
92 if (strcmp(argv[i], "debug") == 0)
93 debug = B_TRUE;
94 else if (strcmp(argv[i], "nowarn") == 0)
95 nowarn = B_TRUE;
96 }
97
98 if ((flags & PAM_PRELIM_CHECK) != 0)
99 return (PAM_IGNORE);
100
101 if ((flags & PAM_UPDATE_AUTHTOK) == 0)
102 return (PAM_SYSTEM_ERR);
103
104 if ((flags & PAM_SILENT) != 0)
105 nowarn = B_TRUE;
106
107 if (debug)
108 __pam_log(LOG_AUTH | LOG_DEBUG,
109 "pam_smb_passwd: storing authtok");
110
111 (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
112 (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
113
114 if (user == NULL || *user == '\0') {
115 __pam_log(LOG_AUTH | LOG_ERR,
116 "pam_smb_passwd: username is empty");
117 return (PAM_USER_UNKNOWN);
118 }
119
120 (void) pam_get_item(pamh, PAM_AUTHTOK, (const void **)&newpw);
121 if (newpw == NULL) {
122 /*
123 * A module on the stack has removed PAM_AUTHTOK. We fail
124 */
125 return (PAM_AUTHTOK_ERR);
126 }
127
128 /* Check to see if this is a local user */
129 files_rep.type = "files";
130 files_rep.scope = NULL;
131 files_rep.scope_len = 0;
132 res = __user_to_authenticate(user, &files_rep, &local_user,
133 &privileged);
134 if (res != PWU_SUCCESS) {
135 switch (res) {
136 case PWU_NOT_FOUND:
137 /* if not a local user, ignore */
138 if (debug) {
139 __pam_log(LOG_AUTH | LOG_DEBUG,
140 "pam_smb_passwd: %s is not local", user);
141 }
142 return (PAM_IGNORE);
143 case PWU_DENIED:
144 return (PAM_PERM_DENIED);
145 }
146 return (PAM_SYSTEM_ERR);
147 }
148
149 smb_pwd_init(B_FALSE);
150
151 res = smb_pwd_setpasswd(user, newpw);
152
153 smb_pwd_fini();
154
155 /*
156 * now map the various return states to user messages
157 * and PAM return codes.
158 */
159 switch (res) {
160 case SMB_PWE_SUCCESS:
161 info(nowarn, pamh, dgettext(TEXT_DOMAIN,
162 "%s: SMB password successfully changed for %s"),
163 service, user);
164 return (PAM_SUCCESS);
165
166 case SMB_PWE_STAT_FAILED:
167 __pam_log(LOG_AUTH | LOG_ERR,
168 "%s: stat of SMB password file failed", service);
169 return (PAM_SYSTEM_ERR);
170
171 case SMB_PWE_OPEN_FAILED:
172 case SMB_PWE_WRITE_FAILED:
173 case SMB_PWE_CLOSE_FAILED:
174 case SMB_PWE_UPDATE_FAILED:
175 error(nowarn, pamh, dgettext(TEXT_DOMAIN,
176 "%s: Unexpected failure. SMB password database unchanged."),
177 service);
178 return (PAM_SYSTEM_ERR);
179
180 case SMB_PWE_BUSY:
181 error(nowarn, pamh, dgettext(TEXT_DOMAIN,
182 "%s: SMB password database busy. Try again later."),
183 service);
184
185 return (PAM_AUTHTOK_LOCK_BUSY);
186
187 case SMB_PWE_USER_UNKNOWN:
188 error(nowarn, pamh, dgettext(TEXT_DOMAIN,
189 "%s: %s does not exist."), service, user);
190 return (PAM_USER_UNKNOWN);
191
192 case SMB_PWE_USER_DISABLE:
193 error(nowarn, pamh, dgettext(TEXT_DOMAIN,
194 "%s: %s is disable. SMB password database unchanged."),
195 service, user);
196 return (PAM_IGNORE);
197
198 case SMB_PWE_DENIED:
199 return (PAM_PERM_DENIED);
200
201 default:
202 res = PAM_SYSTEM_ERR;
203 break;
204 }
205
206 return (res);
207 }
208