summaryrefslogtreecommitdiffstats
path: root/src/windows/cns/kpasswd.c
blob: 2f1327126c3e15d83cf9642064477c1a62d9ce1d (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
/*
 * 
 * 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;
  char foo[1024];
  int i;

  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 crud 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);
  ccache = (krb5_ccache)NULL;

  /*
   * Establish the connection.
   */
  kret = krb5_adm_connect(k5context, name, NULL, opasswd, &conn_socket,
			  &auth_context, &ccache, "kadm.tk", 0);
  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 */