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