xref: /illumos-gate/usr/src/cmd/power/pm_pam_conv.c (revision b1593d50e783f7d66722dde093752b74ffa95176)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * This file has all of the PAM related code for sys-suspend.  It is
28  * part of it's own file, as these could be part of some bigger item
29  * that can handle generic PAM facilities (certainly the getinput()
30  * function could be in a common library).  However, as that does not
31  * yet exist, we replicate it here so we can get the job done.
32  */
33 
34 #define	__EXTENSIONS__	/* to expose flockfile and friends in stdio.h */
35 #include <errno.h>
36 #include <libgen.h>
37 #include <malloc.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <strings.h>
42 #include <stropts.h>
43 #include <unistd.h>
44 #include <termio.h>
45 
46 #include <security/pam_appl.h>
47 
48 static int ctl_c;	/* was the conversation interrupted? */
49 
50 /* ARGSUSED 1 */
51 static void
52 interrupt(int x)
53 {
54 	ctl_c = 1;
55 }
56 
57 /*
58  * getinput -- read user input from stdin abort on ^C
59  *
60  *	Entry	noecho == TRUE, don't echo input.
61  *
62  *	Exit	User's input.
63  *		If interrupted, send SIGINT to caller for processing.
64  */
65 static char *
66 getinput(int noecho)
67 {
68 	struct termio tty;
69 	unsigned short tty_flags;
70 	char input[PAM_MAX_RESP_SIZE + 1];
71 	int c;
72 	int i = 0;
73 	void (*sig)(int);
74 
75 	ctl_c = 0;
76 	sig = signal(SIGINT, interrupt);
77 	if (noecho) {
78 		(void) ioctl(fileno(stdin), TCGETA, &tty);
79 		tty_flags = tty.c_lflag;
80 		tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
81 		(void) ioctl(fileno(stdin), TCSETAF, &tty);
82 	}
83 	/* go to end, but don't overflow PAM_MAX_RESP_SIZE */
84 	flockfile(stdin);
85 	while (ctl_c == 0 &&
86 	    (c = getchar_unlocked()) != '\n' &&
87 	    c != '\r' &&
88 	    c != EOF) {
89 		if (i < PAM_MAX_RESP_SIZE) {
90 			input[i++] = (char)c;
91 		}
92 	}
93 	funlockfile(stdin);
94 	input[i] = '\0';
95 	if (noecho) {
96 		tty.c_lflag = tty_flags;
97 		(void) ioctl(fileno(stdin), TCSETAW, &tty);
98 		(void) fputc('\n', stdout);
99 	}
100 	(void) signal(SIGINT, sig);
101 	if (ctl_c == 1)
102 		(void) kill(getpid(), SIGINT);
103 
104 	return (strdup(input));
105 }
106 
107 /*
108  * Service modules don't clean up responses if an error is returned.
109  * Free responses here.
110  */
111 static void
112 free_resp(int num_msg, struct pam_response *pr)
113 {
114 	int i;
115 	struct pam_response *r = pr;
116 
117 	if (pr == NULL)
118 		return;
119 
120 	for (i = 0; i < num_msg; i++, r++) {
121 
122 		if (r->resp) {
123 			/* clear before freeing -- may be a password */
124 			bzero(r->resp, strlen(r->resp));
125 			free(r->resp);
126 			r->resp = NULL;
127 		}
128 	}
129 	free(pr);
130 }
131 
132 /* ARGSUSED */
133 int
134 pam_tty_conv(int num_msg, struct pam_message **mess,
135     struct pam_response **resp, void *my_data)
136 {
137 	struct pam_message *m = *mess;
138 	struct pam_response *r = calloc(num_msg, sizeof (struct pam_response));
139 	int i;
140 
141 	if (num_msg >= PAM_MAX_NUM_MSG) {
142 		(void) fprintf(stderr, "too many messages %d >= %d\n",
143 		    num_msg, PAM_MAX_NUM_MSG);
144 		free(r);
145 		*resp = NULL;
146 		return (PAM_CONV_ERR);
147 	}
148 
149 	/* Talk it out */
150 	*resp = r;
151 	for (i = 0; i < num_msg; i++) {
152 		int echo_off;
153 
154 		/* bad message from service module */
155 		if (m->msg == NULL) {
156 			(void) fprintf(stderr, "message[%d]: %d/NULL\n",
157 			    i, m->msg_style);
158 			goto err;
159 		}
160 
161 		/*
162 		 * fix up final newline:
163 		 * 	removed for prompts
164 		 * 	added back for messages
165 		 */
166 		if (m->msg[strlen(m->msg)] == '\n')
167 			m->msg[strlen(m->msg)] = '\0';
168 
169 		r->resp = NULL;
170 		r->resp_retcode = 0;
171 		echo_off = 0;
172 		switch (m->msg_style) {
173 
174 		case PAM_PROMPT_ECHO_OFF:
175 			echo_off = 1;
176 			/*FALLTHROUGH*/
177 
178 		case PAM_PROMPT_ECHO_ON:
179 			(void) fputs(m->msg, stdout);
180 
181 			r->resp = getinput(echo_off);
182 			break;
183 
184 		case PAM_ERROR_MSG:
185 			(void) fputs(m->msg, stderr);
186 			(void) fputc('\n', stderr);
187 			break;
188 
189 		case PAM_TEXT_INFO:
190 			(void) fputs(m->msg, stdout);
191 			(void) fputc('\n', stdout);
192 			break;
193 
194 		default:
195 			(void) fprintf(stderr, "message[%d]: unknown type "
196 			    "%d/val=\"%s\"\n",
197 			    i, m->msg_style, m->msg);
198 			/* error, service module won't clean up */
199 			goto err;
200 		}
201 		if (errno == EINTR)
202 			goto err;
203 
204 		/* next message/response */
205 		m++;
206 		r++;
207 	}
208 	return (PAM_SUCCESS);
209 
210 err:
211 	free_resp(i, r);
212 	*resp = NULL;
213 	return (PAM_CONV_ERR);
214 }
215