2 * $Id: uams_pam.c,v 1.24 2010-03-30 10:25:49 franklahm Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
6 * All Rights Reserved. See COPYRIGHT.
11 #endif /* HAVE_CONFIG_H */
17 #endif /* HAVE_UNISTD_H */
19 #include <atalk/logger.h>
21 #ifdef HAVE_SECURITY_PAM_APPL_H
22 #include <security/pam_appl.h>
24 #ifdef HAVE_PAM_PAM_APPL_H
25 #include <pam/pam_appl.h>
28 #include <atalk/afp.h>
29 #include <atalk/uam.h>
30 #include <atalk/util.h>
35 #define MIN(a,b) ((a) < (b) ? (a) : (b))
39 /* Static variables used to communicate between the conversation function
40 * and the server_login function
42 static pam_handle_t *pamh = NULL;
43 static const char *hostname;
44 static char *PAM_username;
45 static char *PAM_password;
47 /*XXX in etc/papd/file.h */
49 extern UAM_MODULE_EXPORT void append(struct papfile *, const char *, int);
51 /* PAM conversation function
52 * Here we assume (for now, at least) that echo on means login name, and
53 * echo off means password.
55 static int PAM_conv (int num_msg,
56 const struct pam_message **msg,
57 struct pam_response **resp,
58 void *appdata_ptr _U_)
60 struct pam_response *reply;
63 #define COPY_STRING(s) (s) ? strdup(s) : NULL
68 reply = (struct pam_response *)
69 calloc(num_msg, sizeof(struct pam_response));
74 for (count = 0; count < num_msg; count++) {
77 switch (msg[count]->msg_style) {
78 case PAM_PROMPT_ECHO_ON:
79 if (!(string = COPY_STRING(PAM_username)))
82 case PAM_PROMPT_ECHO_OFF:
83 if (!(string = COPY_STRING(PAM_password)))
87 #ifdef PAM_BINARY_PROMPT
88 case PAM_BINARY_PROMPT:
89 #endif /* PAM_BINARY_PROMPT */
98 reply[count].resp_retcode = 0;
99 reply[count].resp = string;
108 for (count = 0; count < num_msg; count++) {
109 if (!reply[count].resp)
111 switch (msg[count]->msg_style) {
112 case PAM_PROMPT_ECHO_OFF:
113 case PAM_PROMPT_ECHO_ON:
114 free(reply[count].resp);
122 static struct pam_conv PAM_conversation = {
127 static int login(void *obj, char *username, int ulen, struct passwd **uam_pwd,
128 char *ibuf, size_t ibuflen _U_,
129 char *rbuf _U_, size_t *rbuflen _U_)
134 if (uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME,
135 (void *) &hostname, NULL) < 0)
137 LOG(log_info, logtype_uams, "uams_pam.c :PAM: unable to retrieve client hostname");
141 ibuf[ PASSWDLEN ] = '\0';
143 if (( pwd = uam_getname(obj, username, ulen)) == NULL ) {
144 return AFPERR_NOTAUTH;
147 LOG(log_info, logtype_uams, "cleartext login: %s", username);
148 PAM_username = username;
149 PAM_password = ibuf; /* Set these things up for the conv function */
151 err = AFPERR_NOTAUTH;
152 PAM_error = pam_start("netatalk", username, &PAM_conversation,
154 if (PAM_error != PAM_SUCCESS)
157 pam_set_item(pamh, PAM_TTY, "afpd");
158 pam_set_item(pamh, PAM_RHOST, hostname);
159 /* use PAM_DISALLOW_NULL_AUTHTOK if passwdminlen > 0 */
160 PAM_error = pam_authenticate(pamh,0);
161 if (PAM_error != PAM_SUCCESS) {
162 if (PAM_error == PAM_MAXTRIES)
163 err = AFPERR_PWDEXPR;
167 PAM_error = pam_acct_mgmt(pamh, 0);
168 if (PAM_error != PAM_SUCCESS) {
169 if (PAM_error == PAM_NEW_AUTHTOK_REQD) /* Password change required */
170 err = AFPERR_PWDEXPR;
171 #ifdef PAM_AUTHTOKEN_REQD
172 else if (PAM_error == PAM_AUTHTOKEN_REQD)
173 err = AFPERR_PWDCHNG;
174 #endif /* PAM_AUTHTOKEN_REQD */
179 #ifndef PAM_CRED_ESTABLISH
180 #define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED
181 #endif /* PAM_CRED_ESTABLISH */
182 PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH);
183 if (PAM_error != PAM_SUCCESS)
186 PAM_error = pam_open_session(pamh, 0);
187 if (PAM_error != PAM_SUCCESS)
192 if (err == AFPERR_PWDEXPR)
198 pam_end(pamh, PAM_error);
203 /* --------------------------
206 static int pam_login(void *obj, struct passwd **uam_pwd,
207 char *ibuf, size_t ibuflen,
208 char *rbuf, size_t *rbuflen)
215 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) {
219 len = (unsigned char) *ibuf++;
221 return( AFPERR_PARAM );
224 memcpy(username, ibuf, len );
227 username[ len ] = '\0';
229 if ((unsigned long) ibuf & 1) /* pad character */
231 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
234 /* ----------------------------- */
235 static int pam_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
236 char *ibuf, size_t ibuflen,
237 char *rbuf, size_t *rbuflen)
245 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0)
251 memcpy(&temp16, uname, sizeof(temp16));
254 if (!len || len > ulen ) {
255 return( AFPERR_PARAM );
257 memcpy(username, uname +2, len );
258 username[ len ] = '\0';
260 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
264 static void pam_logout(void) {
265 pam_close_session(pamh, 0);
271 static int pam_changepw(void *obj _U_, char *username,
272 struct passwd *pwd _U_, char *ibuf, size_t ibuflen _U_,
273 char *rbuf _U_, size_t *rbuflen _U_)
275 char pw[PASSWDLEN + 1];
281 memcpy(pw, ibuf, PASSWDLEN);
282 memset(ibuf, 0, PASSWDLEN);
283 pw[PASSWDLEN] = '\0';
285 /* let's do a quick check for the same password */
286 if (memcmp(pw, ibuf + PASSWDLEN, PASSWDLEN) == 0)
287 return AFPERR_PWDSAME;
289 /* Set these things up for the conv function */
290 PAM_username = username;
293 PAM_error = pam_start("netatalk", username, &PAM_conversation,
295 if (PAM_error != PAM_SUCCESS)
297 pam_set_item(lpamh, PAM_TTY, "afpd");
298 pam_set_item(lpamh, PAM_RHOST, hostname);
300 /* we might need to do this as root */
303 PAM_error = pam_authenticate(lpamh,0);
304 if (PAM_error != PAM_SUCCESS) {
306 pam_end(lpamh, PAM_error);
307 return AFPERR_NOTAUTH;
313 ibuf[PASSWDLEN] = '\0';
315 /* this really does need to be done as root */
316 PAM_error = pam_chauthtok(lpamh, 0);
317 seteuid(uid); /* un-root ourselves. */
318 memset(pw, 0, PASSWDLEN);
319 memset(ibuf, 0, PASSWDLEN);
320 if (PAM_error != PAM_SUCCESS) {
321 pam_end(lpamh, PAM_error);
322 return AFPERR_ACCESS;
330 /* Printer ClearTxtUAM login */
331 static int pam_printer(char *start, char *stop, char *username, struct papfile *out)
335 char password[PASSWDLEN + 1] = "\0";
336 static const char *loginok = "0\r";
339 data = (char *)malloc(stop - start + 1);
341 LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: malloc");
345 strlcpy(data, start, stop - start + 1);
347 /* We are looking for the following format in data:
348 * (username) (password)
350 * Let's hope username doesn't contain ") ("!
353 /* Parse input for username in () */
354 if ((p = strchr(data, '(' )) == NULL) {
355 LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: username not found in string");
360 if ((q = strstr(p, ") (" )) == NULL) {
361 LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: username not found in string");
365 memcpy(username, p, MIN(UAM_USERNAMELEN, q - p) );
367 /* Parse input for password in next () */
369 if ((q = strrchr(p, ')' )) == NULL) {
370 LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: password not found in string");
374 memcpy(password, p, MIN(PASSWDLEN, (q - p)) );
376 /* Done copying username and password, clean up */
379 if (( pwd = uam_getname(NULL, username, strlen(username))) == NULL ) {
380 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: ( %s ) not found ",
385 if (uam_checkuser(pwd) < 0) {
386 /* syslog of error happens in uam_checkuser */
390 PAM_username = username;
391 PAM_password = password;
393 PAM_error = pam_start("netatalk", username, &PAM_conversation,
395 if (PAM_error != PAM_SUCCESS) {
396 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s",
397 username, pam_strerror(pamh, PAM_error));
398 pam_end(pamh, PAM_error);
403 pam_set_item(pamh, PAM_TTY, "papd");
404 pam_set_item(pamh, PAM_RHOST, hostname);
405 PAM_error = pam_authenticate(pamh,0);
406 if (PAM_error != PAM_SUCCESS) {
407 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s",
408 username, pam_strerror(pamh, PAM_error));
409 pam_end(pamh, PAM_error);
414 PAM_error = pam_acct_mgmt(pamh, 0);
415 if (PAM_error != PAM_SUCCESS) {
416 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s",
417 username, pam_strerror(pamh, PAM_error));
418 pam_end(pamh, PAM_error);
423 PAM_error = pam_open_session(pamh, 0);
424 if (PAM_error != PAM_SUCCESS) {
425 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s",
426 username, pam_strerror(pamh, PAM_error));
427 pam_end(pamh, PAM_error);
432 /* Login successful, but no need to hang onto it,
433 so logout immediately */
434 append(out, loginok, strlen(loginok));
435 LOG(log_info, logtype_uams, "Login ClearTxtUAM: %s", username);
436 pam_close_session(pamh, 0);
444 static int uam_setup(const char *path)
446 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Cleartxt Passwrd",
447 pam_login, NULL, pam_logout, pam_login_ext) < 0)
450 if (uam_register(UAM_SERVER_CHANGEPW, path, "Cleartxt Passwrd",
452 uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
456 if (uam_register(UAM_SERVER_PRINTAUTH, path, "ClearTxtUAM",
464 static void uam_cleanup(void)
466 uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd");
467 uam_unregister(UAM_SERVER_CHANGEPW, "Cleartxt Passwrd");
468 uam_unregister(UAM_SERVER_PRINTAUTH, "ClearTxtUAM");
471 UAM_MODULE_EXPORT struct uam_export uams_clrtxt = {
474 uam_setup, uam_cleanup
477 UAM_MODULE_EXPORT struct uam_export uams_pam = {
480 uam_setup, uam_cleanup