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