1/* 2 * $Id: uams_pam.c,v 1.24 2010-03-30 10:25:49 franklahm Exp $ 3 * 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. 7 */ 8 9#ifdef HAVE_CONFIG_H 10#include "config.h" 11#endif /* HAVE_CONFIG_H */ 12 13#include <stdio.h> 14#include <stdlib.h> 15#ifdef HAVE_UNISTD_H 16#include <unistd.h> 17#endif /* HAVE_UNISTD_H */ 18 19/* STDC check */ 20#if STDC_HEADERS 21#include <string.h> 22#else /* STDC_HEADERS */ 23#ifndef HAVE_STRCHR 24#define strchr index 25#define strrchr index 26#endif /* HAVE_STRCHR */ 27char *strchr (), *strrchr (); 28#ifndef HAVE_MEMCPY 29#define memcpy(d,s,n) bcopy ((s), (d), (n)) 30#define memmove(d,s,n) bcopy ((s), (d), (n)) 31#endif /* ! HAVE_MEMCPY */ 32#endif /* STDC_HEADERS */ 33 34#include <atalk/logger.h> 35 36#ifdef HAVE_SECURITY_PAM_APPL_H 37#include <security/pam_appl.h> 38#endif 39#ifdef HAVE_PAM_PAM_APPL_H 40#include <pam/pam_appl.h> 41#endif 42 43#include <atalk/afp.h> 44#include <atalk/uam.h> 45#include <atalk/util.h> 46 47#define PASSWDLEN 8 48 49#ifndef MIN 50#define MIN(a,b) ((a) < (b) ? (a) : (b)) 51#endif /* MIN */ 52 53 54/* Static variables used to communicate between the conversation function 55 * and the server_login function 56 */ 57static pam_handle_t *pamh = NULL; 58static const char *hostname; 59static char *PAM_username; 60static char *PAM_password; 61 62/*XXX in etc/papd/file.h */ 63struct papfile; 64extern UAM_MODULE_EXPORT void append(struct papfile *, const char *, int); 65 66/* PAM conversation function 67 * Here we assume (for now, at least) that echo on means login name, and 68 * echo off means password. 69 */ 70static int PAM_conv (int num_msg, 71 const struct pam_message **msg, 72 struct pam_response **resp, 73 void *appdata_ptr _U_) 74{ 75 struct pam_response *reply; 76 int count; 77 78#define COPY_STRING(s) (s) ? strdup(s) : NULL 79 80 if (num_msg < 1) 81 return PAM_CONV_ERR; 82 83 reply = (struct pam_response *) 84 calloc(num_msg, sizeof(struct pam_response)); 85 86 if (!reply) 87 return PAM_CONV_ERR; 88 89 for (count = 0; count < num_msg; count++) { 90 char *string = NULL; 91 92 switch (msg[count]->msg_style) { 93 case PAM_PROMPT_ECHO_ON: 94 if (!(string = COPY_STRING(PAM_username))) 95 goto pam_fail_conv; 96 break; 97 case PAM_PROMPT_ECHO_OFF: 98 if (!(string = COPY_STRING(PAM_password))) 99 goto pam_fail_conv; 100 break; 101 case PAM_TEXT_INFO: 102#ifdef PAM_BINARY_PROMPT 103 case PAM_BINARY_PROMPT: 104#endif /* PAM_BINARY_PROMPT */ 105 /* ignore it... */ 106 break; 107 case PAM_ERROR_MSG: 108 default: 109 goto pam_fail_conv; 110 } 111 112 if (string) { 113 reply[count].resp_retcode = 0; 114 reply[count].resp = string; 115 string = NULL; 116 } 117 } 118 119 *resp = reply; 120 return PAM_SUCCESS; 121 122pam_fail_conv: 123 for (count = 0; count < num_msg; count++) { 124 if (!reply[count].resp) 125 continue; 126 switch (msg[count]->msg_style) { 127 case PAM_PROMPT_ECHO_OFF: 128 case PAM_PROMPT_ECHO_ON: 129 free(reply[count].resp); 130 break; 131 } 132 } 133 free(reply); 134 return PAM_CONV_ERR; 135} 136 137static struct pam_conv PAM_conversation = { 138 &PAM_conv, 139 NULL 140}; 141 142static int login(void *obj, char *username, int ulen, struct passwd **uam_pwd, 143 char *ibuf, size_t ibuflen _U_, 144 char *rbuf _U_, size_t *rbuflen _U_) 145{ 146 struct passwd *pwd; 147 int err, PAM_error; 148 149 if (uam_afpserver_option(obj, UAM_OPTION_CLIENTNAME, 150 (void *) &hostname, NULL) < 0) 151 { 152 LOG(log_info, logtype_uams, "uams_pam.c :PAM: unable to retrieve client hostname"); 153 hostname = NULL; 154 } 155 156 ibuf[ PASSWDLEN ] = '\0'; 157 158 if (( pwd = uam_getname(obj, username, ulen)) == NULL ) { 159 return AFPERR_NOTAUTH; 160 } 161 162 LOG(log_info, logtype_uams, "cleartext login: %s", username); 163 PAM_username = username; 164 PAM_password = ibuf; /* Set these things up for the conv function */ 165 166 err = AFPERR_NOTAUTH; 167 PAM_error = pam_start("netatalk", username, &PAM_conversation, 168 &pamh); 169 if (PAM_error != PAM_SUCCESS) 170 goto login_err; 171 172 pam_set_item(pamh, PAM_TTY, "afpd"); 173 pam_set_item(pamh, PAM_RHOST, hostname); 174 /* use PAM_DISALLOW_NULL_AUTHTOK if passwdminlen > 0 */ 175 PAM_error = pam_authenticate(pamh,0); 176 if (PAM_error != PAM_SUCCESS) { 177 if (PAM_error == PAM_MAXTRIES) 178 err = AFPERR_PWDEXPR; 179 goto login_err; 180 } 181 182 PAM_error = pam_acct_mgmt(pamh, 0); 183 if (PAM_error != PAM_SUCCESS) { 184 if (PAM_error == PAM_NEW_AUTHTOK_REQD) /* Password change required */ 185 err = AFPERR_PWDEXPR; 186#ifdef PAM_AUTHTOKEN_REQD 187 else if (PAM_error == PAM_AUTHTOKEN_REQD) 188 err = AFPERR_PWDCHNG; 189#endif /* PAM_AUTHTOKEN_REQD */ 190 else 191 goto login_err; 192 } 193 194#ifndef PAM_CRED_ESTABLISH 195#define PAM_CRED_ESTABLISH PAM_ESTABLISH_CRED 196#endif /* PAM_CRED_ESTABLISH */ 197 PAM_error = pam_setcred(pamh, PAM_CRED_ESTABLISH); 198 if (PAM_error != PAM_SUCCESS) 199 goto login_err; 200 201 PAM_error = pam_open_session(pamh, 0); 202 if (PAM_error != PAM_SUCCESS) 203 goto login_err; 204 205 *uam_pwd = pwd; 206 207 if (err == AFPERR_PWDEXPR) 208 return err; 209 210 return AFP_OK; 211 212login_err: 213 pam_end(pamh, PAM_error); 214 pamh = NULL; 215 return err; 216} 217 218/* -------------------------- 219 cleartxt login 220*/ 221static int pam_login(void *obj, struct passwd **uam_pwd, 222 char *ibuf, size_t ibuflen, 223 char *rbuf, size_t *rbuflen) 224{ 225 char *username; 226 size_t len, ulen; 227 228 *rbuflen = 0; 229 230 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) { 231 return AFPERR_MISC; 232 } 233 234 len = (unsigned char) *ibuf++; 235 if ( len > ulen ) { 236 return( AFPERR_PARAM ); 237 } 238 239 memcpy(username, ibuf, len ); 240 ibuf += len; 241 242 username[ len ] = '\0'; 243 244 if ((unsigned long) ibuf & 1) /* pad character */ 245 ++ibuf; 246 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen)); 247} 248 249/* ----------------------------- */ 250static int pam_login_ext(void *obj, char *uname, struct passwd **uam_pwd, 251 char *ibuf, size_t ibuflen, 252 char *rbuf, size_t *rbuflen) 253{ 254 char *username; 255 size_t len, ulen; 256 u_int16_t temp16; 257 258 *rbuflen = 0; 259 260 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, (void *) &username, &ulen) < 0) 261 return AFPERR_MISC; 262 263 if (*uname != 3) 264 return AFPERR_PARAM; 265 uname++; 266 memcpy(&temp16, uname, sizeof(temp16)); 267 len = ntohs(temp16); 268 269 if (!len || len > ulen ) { 270 return( AFPERR_PARAM ); 271 } 272 memcpy(username, uname +2, len ); 273 username[ len ] = '\0'; 274 275 return (login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen)); 276} 277 278/* logout */ 279static void pam_logout(void) { 280 pam_close_session(pamh, 0); 281 pam_end(pamh, 0); 282 pamh = NULL; 283} 284 285/* change passwd */ 286static int pam_changepw(void *obj _U_, char *username, 287 struct passwd *pwd _U_, char *ibuf, size_t ibuflen _U_, 288 char *rbuf _U_, size_t *rbuflen _U_) 289{ 290 char pw[PASSWDLEN + 1]; 291 pam_handle_t *lpamh; 292 uid_t uid; 293 int PAM_error; 294 295 /* old password */ 296 memcpy(pw, ibuf, PASSWDLEN); 297 memset(ibuf, 0, PASSWDLEN); 298 pw[PASSWDLEN] = '\0'; 299 300 /* let's do a quick check for the same password */ 301 if (memcmp(pw, ibuf + PASSWDLEN, PASSWDLEN) == 0) 302 return AFPERR_PWDSAME; 303 304 /* Set these things up for the conv function */ 305 PAM_username = username; 306 PAM_password = pw; 307 308 PAM_error = pam_start("netatalk", username, &PAM_conversation, 309 &lpamh); 310 if (PAM_error != PAM_SUCCESS) 311 return AFPERR_PARAM; 312 pam_set_item(lpamh, PAM_TTY, "afpd"); 313 pam_set_item(lpamh, PAM_RHOST, hostname); 314 315 /* we might need to do this as root */ 316 uid = geteuid(); 317 seteuid(0); 318 PAM_error = pam_authenticate(lpamh,0); 319 if (PAM_error != PAM_SUCCESS) { 320 seteuid(uid); 321 pam_end(lpamh, PAM_error); 322 return AFPERR_NOTAUTH; 323 } 324 325 /* new password */ 326 ibuf += PASSWDLEN; 327 PAM_password = ibuf; 328 ibuf[PASSWDLEN] = '\0'; 329 330 /* this really does need to be done as root */ 331 PAM_error = pam_chauthtok(lpamh, 0); 332 seteuid(uid); /* un-root ourselves. */ 333 memset(pw, 0, PASSWDLEN); 334 memset(ibuf, 0, PASSWDLEN); 335 if (PAM_error != PAM_SUCCESS) { 336 pam_end(lpamh, PAM_error); 337 return AFPERR_ACCESS; 338 } 339 340 pam_end(lpamh, 0); 341 return AFP_OK; 342} 343 344 345/* Printer ClearTxtUAM login */ 346static int pam_printer(char *start, char *stop, char *username, struct papfile *out) 347{ 348 int PAM_error; 349 char *data, *p, *q; 350 char password[PASSWDLEN + 1] = "\0"; 351 static const char *loginok = "0\r"; 352 struct passwd *pwd; 353 354 data = (char *)malloc(stop - start + 1); 355 if (!data) { 356 LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: malloc"); 357 return(-1); 358 } 359 360 strlcpy(data, start, stop - start + 1); 361 362 /* We are looking for the following format in data: 363 * (username) (password) 364 * 365 * Let's hope username doesn't contain ") ("! 366 */ 367 368 /* Parse input for username in () */ 369 if ((p = strchr(data, '(' )) == NULL) { 370 LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: username not found in string"); 371 free(data); 372 return(-1); 373 } 374 p++; 375 if ((q = strstr(p, ") (" )) == NULL) { 376 LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: username not found in string"); 377 free(data); 378 return(-1); 379 } 380 memcpy(username, p, MIN(UAM_USERNAMELEN, q - p) ); 381 382 /* Parse input for password in next () */ 383 p = q + 3; 384 if ((q = strrchr(p, ')' )) == NULL) { 385 LOG(log_info, logtype_uams,"Bad Login ClearTxtUAM: password not found in string"); 386 free(data); 387 return(-1); 388 } 389 memcpy(password, p, MIN(PASSWDLEN, (q - p)) ); 390 391 /* Done copying username and password, clean up */ 392 free(data); 393 394 if (( pwd = uam_getname(NULL, username, strlen(username))) == NULL ) { 395 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: ( %s ) not found ", 396 username); 397 return(-1); 398 } 399 400 if (uam_checkuser(pwd) < 0) { 401 /* syslog of error happens in uam_checkuser */ 402 return(-1); 403 } 404 405 PAM_username = username; 406 PAM_password = password; 407 408 PAM_error = pam_start("netatalk", username, &PAM_conversation, 409 &pamh); 410 if (PAM_error != PAM_SUCCESS) { 411 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 412 username, pam_strerror(pamh, PAM_error)); 413 pam_end(pamh, PAM_error); 414 pamh = NULL; 415 return(-1); 416 } 417 418 pam_set_item(pamh, PAM_TTY, "papd"); 419 pam_set_item(pamh, PAM_RHOST, hostname); 420 PAM_error = pam_authenticate(pamh,0); 421 if (PAM_error != PAM_SUCCESS) { 422 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 423 username, pam_strerror(pamh, PAM_error)); 424 pam_end(pamh, PAM_error); 425 pamh = NULL; 426 return(-1); 427 } 428 429 PAM_error = pam_acct_mgmt(pamh, 0); 430 if (PAM_error != PAM_SUCCESS) { 431 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 432 username, pam_strerror(pamh, PAM_error)); 433 pam_end(pamh, PAM_error); 434 pamh = NULL; 435 return(-1); 436 } 437 438 PAM_error = pam_open_session(pamh, 0); 439 if (PAM_error != PAM_SUCCESS) { 440 LOG(log_info, logtype_uams, "Bad Login ClearTxtUAM: %s: %s", 441 username, pam_strerror(pamh, PAM_error)); 442 pam_end(pamh, PAM_error); 443 pamh = NULL; 444 return(-1); 445 } 446 447 /* Login successful, but no need to hang onto it, 448 so logout immediately */ 449 append(out, loginok, strlen(loginok)); 450 LOG(log_info, logtype_uams, "Login ClearTxtUAM: %s", username); 451 pam_close_session(pamh, 0); 452 pam_end(pamh, 0); 453 pamh = NULL; 454 455 return(0); 456} 457 458 459static int uam_setup(const char *path) 460{ 461 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Cleartxt Passwrd", 462 pam_login, NULL, pam_logout, pam_login_ext) < 0) 463 return -1; 464 465 if (uam_register(UAM_SERVER_CHANGEPW, path, "Cleartxt Passwrd", 466 pam_changepw) < 0) { 467 uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd"); 468 return -1; 469 } 470 471 if (uam_register(UAM_SERVER_PRINTAUTH, path, "ClearTxtUAM", 472 pam_printer) < 0) { 473 return -1; 474 } 475 476 return 0; 477} 478 479static void uam_cleanup(void) 480{ 481 uam_unregister(UAM_SERVER_LOGIN, "Cleartxt Passwrd"); 482 uam_unregister(UAM_SERVER_CHANGEPW, "Cleartxt Passwrd"); 483 uam_unregister(UAM_SERVER_PRINTAUTH, "ClearTxtUAM"); 484} 485 486UAM_MODULE_EXPORT struct uam_export uams_clrtxt = { 487 UAM_MODULE_SERVER, 488 UAM_MODULE_VERSION, 489 uam_setup, uam_cleanup 490}; 491 492UAM_MODULE_EXPORT struct uam_export uams_pam = { 493 UAM_MODULE_SERVER, 494 UAM_MODULE_VERSION, 495 uam_setup, uam_cleanup 496}; 497