* preauth.c (handle_sam_labels):
[krb5.git] / src / lib / krb5 / krb / chpw.c
1 #include <string.h>
2
3 #include "k5-int.h"
4 #include "krb5_err.h"
5 #include "auth_con.h"
6
7 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
8 krb5_mk_chpw_req(context, auth_context, ap_req, passwd, packet)
9      krb5_context context;
10      krb5_auth_context auth_context;
11      krb5_data *ap_req;
12      char *passwd;
13      krb5_data *packet;
14 {
15     krb5_error_code ret;
16     krb5_data clearpw;
17     krb5_data cipherpw;
18     krb5_replay_data replay;
19     char *ptr;
20
21     if (ret = krb5_auth_con_setflags(context, auth_context,
22                                      KRB5_AUTH_CONTEXT_DO_SEQUENCE))
23         return(ret);
24
25     clearpw.length = strlen(passwd);
26     clearpw.data = passwd;
27
28     if (ret = krb5_mk_priv(context, auth_context,
29                            &clearpw, &cipherpw, &replay))
30     return(ret);
31
32     packet->length = 6 + ap_req->length + cipherpw.length;
33     packet->data = (char *) malloc(packet->length);
34     if (packet->data == NULL)
35         return ENOMEM;
36     ptr = packet->data;
37
38     /* length */
39
40     *ptr++ = (packet->length>>8) & 0xff;
41     *ptr++ = packet->length & 0xff;
42
43     /* version == 0x0001 big-endian */
44
45     *ptr++ = 0;
46     *ptr++ = 1;
47
48     /* ap_req length, big-endian */
49
50     *ptr++ = (ap_req->length>>8) & 0xff;
51     *ptr++ = ap_req->length & 0xff;
52
53     /* ap-req data */
54
55     memcpy(ptr, ap_req->data, ap_req->length);
56     ptr += ap_req->length;
57
58     /* krb-priv of password */
59
60     memcpy(ptr, cipherpw.data, cipherpw.length);
61
62     return(0);
63 }
64
65 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
66 krb5_rd_chpw_rep(context, auth_context, packet, result_code, result_data)
67      krb5_context context;
68      krb5_auth_context auth_context;
69      krb5_data *packet;
70      int *result_code;
71      krb5_data *result_data;
72 {
73     char *ptr;
74     int plen, vno;
75     krb5_data ap_rep;
76     krb5_ap_rep_enc_part *ap_rep_enc;
77     krb5_error_code ret;
78     krb5_data cipherresult;
79     krb5_data clearresult;
80     krb5_error *krberror;
81     krb5_replay_data replay;
82     krb5_keyblock *tmp;
83
84     if (packet->length < 4)
85         /* either this, or the server is printing bad messages,
86            or the caller passed in garbage */
87         return(KRB5KRB_AP_ERR_MODIFIED);
88
89     ptr = packet->data;
90
91     /* verify length */
92
93     plen = (*ptr++ & 0xff);
94     plen = (plen<<8) | (*ptr++ & 0xff);
95
96     if (plen != packet->length)
97         return(KRB5KRB_AP_ERR_MODIFIED);
98
99     /* verify version number */
100
101     vno = (*ptr++ & 0xff);
102     vno = (vno<<8) | (*ptr++ & 0xff);
103
104     if (vno != 1)
105         return(KRB5KDC_ERR_BAD_PVNO);
106
107     /* read, check ap-rep length */
108
109     ap_rep.length = (*ptr++ & 0xff);
110     ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);
111
112     if (ptr + ap_rep.length >= packet->data + packet->length)
113         return(KRB5KRB_AP_ERR_MODIFIED);
114
115     if (ap_rep.length) {
116         /* verify ap_rep */
117         ap_rep.data = ptr;
118         ptr += ap_rep.length;
119
120         if (ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc))
121             return(ret);
122
123         krb5_free_ap_rep_enc_part(context, ap_rep_enc);
124
125         /* extract and decrypt the result */
126
127         cipherresult.data = ptr;
128         cipherresult.length = (packet->data + packet->length) - ptr;
129
130         /* XXX there's no api to do this right. The problem is that
131            if there's a remote subkey, it will be used.  This is
132            not what the spec requires */
133
134         tmp = auth_context->remote_subkey;
135         auth_context->remote_subkey = NULL;
136
137         ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
138                            &replay);
139
140         auth_context->remote_subkey = tmp;
141
142         if (ret)
143             return(ret);
144     } else {
145         cipherresult.data = ptr;
146         cipherresult.length = (packet->data + packet->length) - ptr;
147
148         if (ret = krb5_rd_error(context, &cipherresult, &krberror))
149             return(ret);
150
151         clearresult = krberror->e_data;
152     }
153
154     if (clearresult.length < 2) {
155         ret = KRB5KRB_AP_ERR_MODIFIED;
156         goto cleanup;
157     }
158
159     ptr = clearresult.data;
160
161     *result_code = (*ptr++ & 0xff);
162     *result_code = (*result_code<<8) | (*ptr++ & 0xff);
163
164     if ((*result_code < KRB5_KPASSWD_SUCCESS) ||
165         (*result_code > KRB5_KPASSWD_SOFTERROR)) {
166         ret = KRB5KRB_AP_ERR_MODIFIED;
167         goto cleanup;
168     }
169
170     /* all success replies should be authenticated/encrypted */
171
172     if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
173         ret = KRB5KRB_AP_ERR_MODIFIED;
174         goto cleanup;
175     }
176
177     result_data->length = (clearresult.data + clearresult.length) - ptr;
178
179     if (result_data->length) {
180         result_data->data = (char *) malloc(result_data->length);
181         if (result_data->data == NULL) {
182             ret = ENOMEM;
183             goto cleanup;
184         }
185         memcpy(result_data->data, ptr, result_data->length);
186     } else {
187         result_data->data = NULL;
188     }
189
190     ret = 0;
191
192 cleanup:
193     if (ap_rep.length) {
194         krb5_xfree(clearresult.data);
195     } else {
196         krb5_free_error(context, krberror);
197     }
198
199     return(ret);
200 }
201
202 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
203 krb5_chpw_result_code_string(context, result_code, code_string)
204      krb5_context context;
205      int result_code;
206      char **code_string;
207 {
208    switch (result_code) {
209    case KRB5_KPASSWD_MALFORMED:
210       *code_string = "Malformed request error";
211       break;
212    case KRB5_KPASSWD_HARDERROR:
213       *code_string = "Server error";
214       break;
215    case KRB5_KPASSWD_AUTHERROR:
216       *code_string = "Authentication error";
217       break;
218    case KRB5_KPASSWD_SOFTERROR:
219       *code_string = "Password change rejected";
220       break;
221    default:
222       *code_string = "Password change failed";
223       break;
224    }
225
226    return(0);
227 }