1 /*
2    This program is free software; you can redistribute it and/or
3    modify it under the terms of the GNU General Public License as
4    published by the Free Software Foundation; either version 2, or (at
5    your option) any later version.
6 
7    This program is distributed in the hope that it will be useful, but
8    WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10    General Public License for more details.
11 
12    Copyright (c) Alexey Mahotkin <alexm@hsys.msk.ru> 2002-2004
13 
14    PAM support for checkpassword-pam
15 
16 */
17 
18 #include <config.h>
19 
20 #include "pam-support.h"
21 
22 #include "logging.h"
23 
24 #include <security/pam_appl.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 static const char* global_password;
29 
30 static int
conversation(int num_msg,const struct pam_message ** msgs,struct pam_response ** resp,void * appdata_ptr)31 conversation (int num_msg, const struct pam_message **msgs,
32 	      struct pam_response **resp, void *appdata_ptr)
33 {
34     int i;
35     struct pam_response* responses;
36     (void) appdata_ptr;
37 
38     /* safety check */
39     if (num_msg <= 0) {
40 	fatal("Internal PAM error: num_msgs <= 0");
41 	return PAM_CONV_ERR;
42     }
43 
44     /* allocate array of responses */
45     responses = calloc(num_msg, sizeof(struct pam_response));
46     if (!responses) {
47 	fatal("Out of memory");
48 	return PAM_CONV_ERR;
49     }
50 
51     for (i = 0; i < num_msg; i++) {
52 	const struct pam_message *msg = msgs[i];
53 	struct pam_response* response = &(responses[i]);
54 	char* style = NULL;
55 	switch (msg->msg_style) {
56 	case PAM_PROMPT_ECHO_OFF: style = "PAM_PROMPT_ECHO_OFF"; break;
57 	case PAM_PROMPT_ECHO_ON: style = "PAM_PROMPT_ECHO_ON"; break;
58 	case PAM_ERROR_MSG: style = "PAM_ERROR_MSG"; break;
59 	case PAM_TEXT_INFO: style = "PAM_TEXT_INFO"; break;
60 	default: fatal("Internal error: invalid msg_style: %d", msg->msg_style); break;
61 	}
62 	debugging("conversation(): msg[%d], style %s, msg = \"%s\"", i, style, msg->msg);
63 
64 	switch (msg->msg_style) {
65 	case PAM_PROMPT_ECHO_OFF:
66 	    /* reply with password */
67 	    response->resp = strdup(global_password);
68 	    if (!response->resp)
69 		return PAM_CONV_ERR;
70 	    break;
71 
72 	default:
73 	    fatal("Internal error: unknown message style: '%s'", style);
74 	    return PAM_CONV_ERR;
75 	}
76 	response->resp_retcode = 0;
77     }
78 
79     *resp = responses;
80 
81     return PAM_SUCCESS;
82 }
83 
84 
85 
86 int
authenticate_using_pam(const char * service_name,const char * username,const char * password)87 authenticate_using_pam (const char* service_name,
88 			const char* username,
89 			const char* password)
90 {
91     struct pam_conv pam_conversation = { conversation, NULL };
92     pam_handle_t* pamh;
93     int retval;
94     char *remoteip;
95 
96     /* to be used later from conversation() */
97     global_password = password;
98 
99     /* initialize the PAM library */
100     debugging("Initializing PAM library using service name '%s'", service_name);
101     retval = pam_start(service_name, username, &pam_conversation, &pamh);
102     if (retval != PAM_SUCCESS) {
103 	fatal("Initialization failed: %s", pam_strerror(pamh, retval));
104 	return 111;
105     }
106     debugging("PAM library initialization succeeded");
107 
108     /* provided by tcpserver */
109     remoteip = getenv("TCPREMOTEIP");
110     if (remoteip) {
111 	/* we don't care if this succeeds or not */
112 	pam_set_item(pamh, PAM_RHOST, remoteip);
113     }
114 
115     /* Authenticate the user */
116     retval = pam_authenticate(pamh, 0);
117     if (retval != PAM_SUCCESS) {
118 	/* usually PAM itself logs auth failures, but we need to see
119            how it looks from our side */
120 	if (opt_debugging)
121 	    fatal("Authentication failed: %s", pam_strerror(pamh, retval));
122 	return 1;
123     }
124 
125     debugging("Authentication passed");
126 
127     retval = pam_acct_mgmt(pamh, 0);
128     if (retval != PAM_SUCCESS) {
129 	fatal("PAM account management failed: %s", pam_strerror(pamh, retval));
130 	return 1;
131     }
132     debugging("Account management succeeded");
133 
134     retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
135     if (retval != PAM_SUCCESS) {
136 	fatal("Setting credentials failed: %s", pam_strerror(pamh, retval));
137 	return 1;
138     }
139     debugging("Setting PAM credentials succeeded");
140 
141     retval = pam_open_session(pamh, PAM_SILENT);
142     if (retval != PAM_SUCCESS) {
143 	    fatal("Session opening failed: %s", pam_strerror(pamh, retval));
144 		return 1;
145     }
146     debugging("PAM session opened");
147 
148     retval = pam_close_session(pamh, PAM_SILENT);
149     if (retval != PAM_SUCCESS) {
150 	fatal("Session closing failed: %s", pam_strerror(pamh, retval));
151 	return 1;
152     }
153     debugging("PAM session closed");
154 
155     /* terminate the PAM library */
156     debugging("Terminating PAM library");
157     retval = pam_end(pamh, retval);
158     if (retval != PAM_SUCCESS) {
159 	fatal("Terminating PAM failed: %s", pam_strerror(pamh, retval));
160 	return 1;
161     }
162 
163     return 0;
164 }
165 
166