/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>

#include <security/pam_appl.h>
#include <errno.h>

static int misc_conv(int num_msg, struct pam_message **msg,
    struct pam_response **resp, void *appdata_ptr)

{
	int n = 0;
	char *userPassword;
	struct pam_response *r;
	struct pam_message *m = *msg;
	int pam_status = PAM_SUCCESS;

	*resp = (struct pam_response *)calloc(num_msg,
		sizeof (struct pam_response));

	if (resp == NULL)
		return (PAM_BUF_ERR);

	r = *resp;
	for (n = 0; n < num_msg; n++) {
		switch (m->msg_style)
		{
			case PAM_PROMPT_ECHO_OFF:
				userPassword = (char *)getpassphrase(m->msg);
				r->resp = (char *)strdup(userPassword);
			if (r->resp == NULL) {
				return (PAM_BUF_ERR);
			}
				break;
			case PAM_PROMPT_ECHO_ON:
				printf("%s", m->msg);
				r->resp = (char *)malloc(PAM_MAX_RESP_SIZE);
				fgets(r->resp, PAM_MAX_RESP_SIZE - 1, stdin);
				if (strlen(r->resp) == 1) {
					free(r->resp);
					r->resp = NULL;
					pam_status = PAM_IGNORE;
				} else {
					r->resp[strlen(r->resp) - 1] = '\0';
					m++;
					r++;
				}
				break;
			case PAM_ERROR_MSG:
				if (m->msg != NULL) {
					printf("ECHO_ERROR: %s\n", m->msg);
				} else {
					printf("ECHO_ERROR: NULL MSG\n");
				}
				break;
			case PAM_TEXT_INFO:
				if (m->msg != NULL) {
					printf("TEXT_INFO: %s\n", m->msg);
				} else {
					printf("TEXT_INFO: NULL MSG\n");
				}
				break;
			default:
				if (m->msg != NULL) {
					printf("default: %s\n", m->msg);
				} else {
					printf("default NULL MSG\n");
				}
				break;
		}
		m++;
	}

	return (pam_status);
}

void
main(int argc, char *argv[])
{
	int ret = -1;
	struct pam_conv pconv = {misc_conv, NULL};
	pam_handle_t *pamh = NULL;
	char *service = NULL;
	char *user = NULL;
	char c;

	while ((c = getopt(argc, argv, "s:u:")) != EOF) {
		switch (c) {
		case 's':
			service = strdup(optarg);
			break;
		case 'u':
			user = strdup(optarg);
			break;
		default:
			fprintf(stderr, "Invalid argument\n");
			exit(1);
		}
	}

	if (service == NULL) {
		service = strdup("testapp");
	}

	if ((ret = pam_start(service, user, &pconv, &pamh)) != PAM_SUCCESS) {
		printf("%s\n", pam_strerror(pamh, ret));
		exit(1);
	}

	pam_set_item(pamh, PAM_TTY, ttyname(1));
	fprintf(stderr, "Authenticating...\n");
	ret = pam_authenticate(pamh, 0);
	if (ret != PAM_SUCCESS) {
		fprintf(stderr, "pam_authenticate failed: %s\n",
		    pam_strerror(pamh, ret));
		exit(1);
	}

	fprintf(stderr, "Account management...\n");
	ret = pam_acct_mgmt(pamh, 0);
	if (ret == PAM_NEW_AUTHTOK_REQD) {
		fprintf(stdout, "Expired password needs changing.");
		ret = pam_chauthtok(pamh, 0);
		if (ret != PAM_SUCCESS) {
			fprintf(stderr, "pam_chauthtok failed: %s\n",
			pam_strerror(pamh, ret));
			exit(1);
		}
	} else if (ret != PAM_SUCCESS) {
		fprintf(stderr, "pam_acct_mgmt failed: %s\n",
		    pam_strerror(pamh, ret));
		exit(1);
	}

	fprintf(stderr, "Establishing creds...\n");
	ret = pam_setcred(pamh, PAM_ESTABLISH_CRED);
	if (ret != PAM_SUCCESS) {
		fprintf(stderr, "pam_setcred PAM_ESTALISH_CRED failed: %s\n",
		    pam_strerror(pamh, ret));
	}

	fprintf(stderr, "Opening session...\n");
	ret = pam_open_session(pamh, 0);
	if (ret != PAM_SUCCESS) {
		fprintf(stderr, "pam_open_session failed: %s\n",
		    pam_strerror(pamh, ret));
	}

	/*
	 * Pretend some time has passed, a refresh would normally be
	 * done after a re-authenticate, eg in a screen saver.
	 */
	fprintf(stderr, "Refresh creds...\n");
	ret = pam_setcred(pamh, PAM_REFRESH_CRED);
	if (ret != PAM_SUCCESS) {
		fprintf(stderr, "pam_setcred PAM_REFRESH_CRED failed: %s\n",
		    pam_strerror(pamh, ret));
	}

	fprintf(stderr, "Re-initalizing creds...\n");
	ret = pam_setcred(pamh, PAM_REINITIALIZE_CRED);
	if (ret != PAM_SUCCESS) {
		fprintf(stderr,
		    "pam_setcred PAM_REINITIALIZE_CRED failed: %s\n",
		    pam_strerror(pamh, ret));
	}

	/*
	 * Pretend user is logging out done, clean up and get out.
	 */
	fprintf(stderr, "Removing creds...\n");
	ret = pam_setcred(pamh, PAM_DELETE_CRED);
	if (ret != PAM_SUCCESS) {
		fprintf(stderr, "pam_setcred PAM_DELETE_CRED failed: %s\n",
		    pam_strerror(pamh, ret));
	}

	fprintf(stderr, "Closing session...\n");
	ret = pam_close_session(pamh, 0);
	if (ret != PAM_SUCCESS) {
		fprintf(stderr, "pam_open_session failed: %s\n",
		    pam_strerror(pamh, ret));
	}

	fprintf(stderr, "PAM end.\n");
	pam_end(pamh, ret);
}
