summaryrefslogtreecommitdiffstats
path: root/src/windows/cns/kpasswd.c
blob: 2a79b84f703016ff1cc3b4adc3218feb5306f6cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*+*************************************************************************
** 
** k5passwd
** 
** Changes your password in the Kerberos V5. This should have been
** part of the kadm stuff but we're forced to build a nicer API on top
** of the calls they provide.
** 
***************************************************************************/
#ifdef KRB5
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "krb5.h"
#include "com_err.h"
#include "adm.h"
#include "adm_proto.h"

static const char *kadm_replies[] = {
	"Operation successful",             	/* KRB5_ADM_SUCCESS */
	"Command not recognized",           	/* KRB5_ADM_CMD_UNKNOWN */
	"Password unacceptable to server",  	/* KRB5_ADM_PW_UNACCEPT */
	"Old password incorrect",           	/* KRB5_ADM_BAD_PW */
	"Invalid ticket (TKT_FLAG_INITIAL not set)",/* KRB5_ADM_NOT_IN_TKT */
	"Server refused password change",   	/* KRB5_ADM_CANT_CHANGE */
	"Language not supported",				/* KRB5_ADM_LANG_NOT_SUPPORTED */
};
static const char *kadm_replies_unknown = "UNKNOWN ERROR";
static char errbuf[1024];					/* For response from kadm */

/*+*************************************************************************
** 
** get_admin_response
** 
** Builds into a static buffer the replies sent back by the admin server.
** 
***************************************************************************/
static char *
get_admin_response (
	krb5_int32 		status,						// Type of error
	krb5_int32 		nreplies,					// Size of reply
	krb5_data *		reply)						// Buffer of messages
{
	char *ptr;									// For building the response
	char *end = errbuf + sizeof (errbuf);		// So we don't overflow
	int i;										// Index
	int n;										// Length

	if (status <= KRB5_ADM_LANG_NOT_SUPPORTED)	// Is it of a known type???
		strcpy (errbuf, kadm_replies[status]);
	else
		strcpy (errbuf, kadm_replies_unknown);	// Unknown error type
	ptr = errbuf + strlen (errbuf);				// Point at the end

	if (nreplies > 0) {							// Are there more message?
		*ptr++ = ':';
		*ptr = '\0';
	}

	for (i = 0; i < nreplies; ++i) {			// Append additional messages
		*ptr++ = '\n';

		n = reply[i].length;					// Easier to work with
		if (ptr + n + 2 >= errbuf)				// Check for overflow
			break;
		memcpy (ptr, reply[i].data, n);			// Add the message
		ptr += n;								// Point to the end
		*ptr = '\0';
	}

	return errbuf;
}
/*+*************************************************************************
** 
** keyadmin_send_recieve
** 
** Sends a command to the key admin and reads the reply.
** 
***************************************************************************/
static krb5_error_code
keyadmin_send_receive (
	krb5_context 		k5context,
	int *				conn_socket,
	krb5_auth_context *	auth_context,
	krb5_int32 			nargs,
	krb5_data *			arglist,
	krb5_int32 *		cmd_stat,
	krb5_int32 *		nreplies,
	krb5_data **		reply)
{
	krb5_error_code	kret;

	kret = krb5_send_adm_cmd (k5context, conn_socket, auth_context,
		nargs, 	arglist);

	if (! kret)
		kret = krb5_read_adm_reply (k5context, conn_socket, auth_context,
			cmd_stat, nreplies, reply);

	return kret;
}
/*+*************************************************************************
** 
** k5_change_password
** 
** Bundles all the crude needed to change the password into one file.
** 
***************************************************************************/
krb5_error_code
k5_change_password (
    krb5_context k5context,
	char *user,
	char *realm,
	char *opasswd,
	char *npasswd,
    char **text)
{
	krb5_error_code		kret, kret2;
	krb5_auth_context * auth_context;
	krb5_ccache			ccache;
	int					conn_socket;			/* Socket for talking over */
	krb5_int32			nreplies;
	krb5_data			data[3];
	krb5_data * 		reply;
	krb5_int32			status;
	char *				name;

	*text = NULL;								/* Be safe */
	name = malloc (strlen (user) + strlen (realm) + 2);
	if (name == NULL)
		return ENOMEM;
	sprintf (name, "%s@%s", user, realm);

/*
** Establish the connection.
*/
	kret = krb5_adm_connect (k5context, name,	NULL, opasswd, &conn_socket,
							&auth_context, &ccache);
	if (kret)
		goto done;
/*
** Check to see if it's an acceptable password
*/
	data[0].data = KRB5_ADM_CHECKPW_CMD;
	data[0].length = strlen (data[0].data);
	data[1].data = npasswd;
	data[1].length = strlen (npasswd);

	kret = keyadmin_send_receive (k5context, &conn_socket, auth_context,
		2, data, &status, &nreplies, &reply);
	if (kret) 									/* Some external error */
		goto cleanup;

	if (status != KRB5_ADM_SUCCESS) {			/* Some problem??? */
		kret = status;
		*text = get_admin_response (status, nreplies, reply);
		krb5_free_adm_data (k5context, nreplies, reply);

		goto quit;
	}
	krb5_free_adm_data (k5context, nreplies, reply);

/*
** The new password is ok, so now actually change the password
*/
	data[0].data = KRB5_ADM_CHANGEPW_CMD;
	data[0].length = strlen (data[0].data);
	data[1].data = opasswd;
	data[1].length = strlen (opasswd);
	data[2].data = npasswd;
	data[2].length = strlen (npasswd);

	kret = keyadmin_send_receive (k5context, &conn_socket, auth_context,
		3, data, &status, &nreplies, &reply);
	if (kret)
		goto cleanup;

	if (status != KRB5_ADM_SUCCESS) {
		kret = status;
		*text = get_admin_response (status, nreplies, reply);
		krb5_free_adm_data (k5context, nreplies, reply);

		goto quit;
	}

	krb5_free_adm_data (k5context, nreplies, reply);
/*+
** Need to send quit command.
*/
 quit:
	data[0].data = KRB5_ADM_QUIT_CMD;
	data[0].length = strlen (data[0].data);

	kret2 = keyadmin_send_receive (k5context, &conn_socket, auth_context,
		1, data, &status, &nreplies, &reply);
	if (kret2) {
		if (! kret)
			kret = kret2;
	} else if (status != KRB5_ADM_SUCCESS) {
		if (! kret)
			kret = status;
		if (*text == NULL)
			*text = get_admin_response (status, nreplies, reply);
	}
	krb5_free_adm_data (k5context, nreplies, reply);

 cleanup:
	krb5_adm_disconnect (k5context, &conn_socket, auth_context, ccache);
 done:
	free (name);

	return kret;
}

#endif /* KRB5 */