| 1 | #include "putty.h" |
| 2 | |
| 3 | #ifndef NO_GSSAPI |
| 4 | |
| 5 | #include <string.h> |
| 6 | #include <gssapi/gssapi.h> |
| 7 | #include "sshgss.h" |
| 8 | #include "misc.h" |
| 9 | |
| 10 | static gss_OID_desc putty_gss_mech_krb5_desc = |
| 11 | { 9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; |
| 12 | static gss_OID const putty_gss_mech_krb5 = &putty_gss_mech_krb5_desc; |
| 13 | |
| 14 | typedef struct uxSsh_gss_ctx { |
| 15 | OM_uint32 maj_stat; |
| 16 | OM_uint32 min_stat; |
| 17 | gss_ctx_id_t ctx; |
| 18 | } uxSsh_gss_ctx; |
| 19 | |
| 20 | int ssh_gss_init(void) |
| 21 | { |
| 22 | /* On Windows this tries to load the SSPI library functions. On |
| 23 | Unix we assume we have GSSAPI at runtime if we were linked with |
| 24 | it at compile time */ |
| 25 | return 1; |
| 26 | } |
| 27 | |
| 28 | Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech) |
| 29 | { |
| 30 | /* Copy constant into mech */ |
| 31 | mech->length = putty_gss_mech_krb5->length; |
| 32 | mech->value = putty_gss_mech_krb5->elements; |
| 33 | |
| 34 | return SSH_GSS_OK; |
| 35 | } |
| 36 | |
| 37 | Ssh_gss_stat ssh_gss_import_name(char *host, |
| 38 | Ssh_gss_name *srv_name) |
| 39 | { |
| 40 | OM_uint32 min_stat,maj_stat; |
| 41 | gss_buffer_desc host_buf; |
| 42 | char *pStr; |
| 43 | |
| 44 | pStr = dupcat("host@", host, NULL); |
| 45 | |
| 46 | host_buf.value = pStr; |
| 47 | host_buf.length = strlen(pStr); |
| 48 | |
| 49 | maj_stat = gss_import_name(&min_stat, &host_buf, |
| 50 | GSS_C_NT_HOSTBASED_SERVICE, srv_name); |
| 51 | /* Release buffer */ |
| 52 | sfree(pStr); |
| 53 | if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; |
| 54 | return SSH_GSS_FAILURE; |
| 55 | } |
| 56 | |
| 57 | Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx) |
| 58 | { |
| 59 | uxSsh_gss_ctx *uxctx = snew(uxSsh_gss_ctx); |
| 60 | |
| 61 | uxctx->maj_stat = uxctx->min_stat = GSS_S_COMPLETE; |
| 62 | uxctx->ctx = GSS_C_NO_CONTEXT; |
| 63 | *ctx = (Ssh_gss_ctx) uxctx; |
| 64 | |
| 65 | return SSH_GSS_OK; |
| 66 | } |
| 67 | |
| 68 | Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx, |
| 69 | Ssh_gss_name srv_name, |
| 70 | int to_deleg, |
| 71 | Ssh_gss_buf *recv_tok, |
| 72 | Ssh_gss_buf *send_tok) |
| 73 | { |
| 74 | uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx*) *ctx; |
| 75 | OM_uint32 ret_flags; |
| 76 | |
| 77 | if (to_deleg) to_deleg = GSS_C_DELEG_FLAG; |
| 78 | uxctx->maj_stat = gss_init_sec_context(&uxctx->min_stat, |
| 79 | GSS_C_NO_CREDENTIAL, |
| 80 | &uxctx->ctx, |
| 81 | srv_name, |
| 82 | (gss_OID) putty_gss_mech_krb5, |
| 83 | GSS_C_MUTUAL_FLAG | |
| 84 | GSS_C_INTEG_FLAG | to_deleg, |
| 85 | 0, |
| 86 | GSS_C_NO_CHANNEL_BINDINGS, |
| 87 | recv_tok, |
| 88 | NULL, /* ignore mech type */ |
| 89 | send_tok, |
| 90 | &ret_flags, |
| 91 | NULL); /* ignore time_rec */ |
| 92 | |
| 93 | if (uxctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE; |
| 94 | if (uxctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED; |
| 95 | return SSH_GSS_FAILURE; |
| 96 | } |
| 97 | |
| 98 | Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf) |
| 99 | { |
| 100 | uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) ctx; |
| 101 | OM_uint32 lmin,lmax; |
| 102 | OM_uint32 ccc; |
| 103 | gss_buffer_desc msg_maj=GSS_C_EMPTY_BUFFER; |
| 104 | gss_buffer_desc msg_min=GSS_C_EMPTY_BUFFER; |
| 105 | |
| 106 | /* Return empty buffer in case of failure */ |
| 107 | SSH_GSS_CLEAR_BUF(buf); |
| 108 | |
| 109 | /* get first mesg from GSS */ |
| 110 | ccc=0; |
| 111 | lmax=gss_display_status(&lmin,uxctx->maj_stat,GSS_C_GSS_CODE,(gss_OID) putty_gss_mech_krb5,&ccc,&msg_maj); |
| 112 | |
| 113 | if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE; |
| 114 | |
| 115 | /* get first mesg from Kerberos */ |
| 116 | ccc=0; |
| 117 | lmax=gss_display_status(&lmin,uxctx->min_stat,GSS_C_MECH_CODE,(gss_OID) putty_gss_mech_krb5,&ccc,&msg_min); |
| 118 | |
| 119 | if (lmax != GSS_S_COMPLETE) { |
| 120 | gss_release_buffer(&lmin, &msg_maj); |
| 121 | return SSH_GSS_FAILURE; |
| 122 | } |
| 123 | |
| 124 | /* copy data into buffer */ |
| 125 | buf->length = msg_maj.length + msg_min.length + 1; |
| 126 | buf->value = snewn(buf->length + 1, char); |
| 127 | |
| 128 | /* copy mem */ |
| 129 | memcpy((char *)buf->value, msg_maj.value, msg_maj.length); |
| 130 | ((char *)buf->value)[msg_maj.length] = ' '; |
| 131 | memcpy((char *)buf->value + msg_maj.length + 1, msg_min.value, msg_min.length); |
| 132 | ((char *)buf->value)[buf->length] = 0; |
| 133 | /* free mem & exit */ |
| 134 | gss_release_buffer(&lmin, &msg_maj); |
| 135 | gss_release_buffer(&lmin, &msg_min); |
| 136 | return SSH_GSS_OK; |
| 137 | } |
| 138 | |
| 139 | Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok) |
| 140 | { |
| 141 | OM_uint32 min_stat,maj_stat; |
| 142 | maj_stat = gss_release_buffer(&min_stat, send_tok); |
| 143 | |
| 144 | if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; |
| 145 | return SSH_GSS_FAILURE; |
| 146 | } |
| 147 | |
| 148 | Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx) |
| 149 | { |
| 150 | uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) *ctx; |
| 151 | OM_uint32 min_stat; |
| 152 | OM_uint32 maj_stat=GSS_S_COMPLETE; |
| 153 | |
| 154 | if (uxctx == NULL) return SSH_GSS_FAILURE; |
| 155 | if (uxctx->ctx != GSS_C_NO_CONTEXT) |
| 156 | maj_stat = gss_delete_sec_context(&min_stat,&uxctx->ctx,GSS_C_NO_BUFFER); |
| 157 | sfree(uxctx); |
| 158 | |
| 159 | if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; |
| 160 | return SSH_GSS_FAILURE; |
| 161 | } |
| 162 | |
| 163 | |
| 164 | Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name) |
| 165 | { |
| 166 | OM_uint32 min_stat,maj_stat; |
| 167 | maj_stat = gss_release_name(&min_stat, srv_name); |
| 168 | |
| 169 | if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; |
| 170 | return SSH_GSS_FAILURE; |
| 171 | } |
| 172 | |
| 173 | Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf, |
| 174 | Ssh_gss_buf *hash) |
| 175 | { |
| 176 | uxSsh_gss_ctx *uxctx = (uxSsh_gss_ctx *) ctx; |
| 177 | if (uxctx == NULL) return SSH_GSS_FAILURE; |
| 178 | return gss_get_mic(&(uxctx->min_stat), uxctx->ctx, 0, buf, hash); |
| 179 | } |
| 180 | |
| 181 | Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash) |
| 182 | { |
| 183 | /* On Unix this is the same freeing process as ssh_gss_free_tok. */ |
| 184 | return ssh_gss_free_tok(hash); |
| 185 | } |
| 186 | |
| 187 | #else |
| 188 | |
| 189 | /* Dummy function so this source file defines something if NO_GSSAPI |
| 190 | is defined. */ |
| 191 | |
| 192 | int ssh_gss_init(void) |
| 193 | { |
| 194 | return 1; |
| 195 | } |
| 196 | |
| 197 | #endif |