/* do the sasl negotiation; return -1 if it fails */ int mysasl_negotiate(FILE *in, FILE *out, sasl_conn_t *conn) { char buf[8192]; char chosenmech[128]; const char *data; int len; int r = SASL_FAIL; const char *userid; #ifdef HAVE_GSS_GET_NAME_ATTRIBUTE gss_name_t peer = GSS_C_NO_NAME; #endif /* generate the capability list */ if (mech) { dprintf(2, "forcing use of mechanism %s\n", mech); data = strdup(mech); len = strlen(data); } else { int count; dprintf(1, "generating client mechanism list... "); r = sasl_listmech(conn, NULL, NULL, " ", NULL, &data, (unsigned int *)&len, &count); if (r != SASL_OK) saslfail(r, "generating mechanism list"); dprintf(1, "%d mechanisms\n", count); } /* send capability list to client */ send_string(out, data, len); dprintf(1, "waiting for client mechanism...\n"); len = recv_string(in, chosenmech, sizeof chosenmech); if (len <= 0) { printf("client didn't choose mechanism\n"); fputc('N', out); /* send NO to client */ fflush(out); return -1; } if (mech && strcasecmp(mech, chosenmech)) { printf("client didn't choose mandatory mechanism\n"); fputc('N', out); /* send NO to client */ fflush(out); return -1; } len = recv_string(in, buf, sizeof(buf)); if(len != 1) { saslerr(r, "didn't receive first-send parameter correctly"); fprintf(stderr, "%s\n", sasl_errdetail(conn)); fputc('N', out); fflush(out); return -1; } if(buf[0] == 'Y') { /* receive initial response (if any) */ len = recv_string(in, buf, sizeof(buf)); /* start libsasl negotiation */ r = sasl_server_start(conn, chosenmech, buf, len, &data, (unsigned int *)&len); } else { r = sasl_server_start(conn, chosenmech, NULL, 0, &data, (unsigned int *)&len); } if (r != SASL_OK && r != SASL_CONTINUE) { saslerr(r, "starting SASL negotiation"); fprintf(stderr, "%s\n", sasl_errdetail(conn)); fputc('N', out); /* send NO to client */ fflush(out); return -1; } while (r == SASL_CONTINUE) { if (data) { dprintf(2, "sending response length %d...\n", len); fputc('C', out); /* send CONTINUE to client */ send_string(out, data, len); } else { dprintf(2, "sending null response...\n"); fputc('C', out); /* send CONTINUE to client */ send_string(out, "", 0); } dprintf(1, "waiting for client reply...\n"); len = recv_string(in, buf, sizeof buf); if (len < 0) { printf("client disconnected\n"); return -1; } r = sasl_server_step(conn, buf, len, &data, (unsigned int *)&len); if (r != SASL_OK && r != SASL_CONTINUE) { saslerr(r, "performing SASL negotiation"); fprintf(stderr, "%s\n", sasl_errdetail(conn)); fputc('N', out); /* send NO to client */ fflush(out); return -1; } } if (r != SASL_OK) { saslerr(r, "incorrect authentication"); fprintf(stderr, "%s\n", sasl_errdetail(conn)); fputc('N', out); /* send NO to client */ fflush(out); return -1; } fputc('O', out); /* send OK to client */ fflush(out); dprintf(1, "negotiation complete\n"); r = sasl_getprop(conn, SASL_USERNAME, (const void **) &userid); printf("successful authentication '%s'\n", userid); #ifdef HAVE_GSS_GET_NAME_ATTRIBUTE r = sasl_getprop(conn, SASL_GSS_PEER_NAME, (const void **) &peer); if (peer != GSS_C_NO_NAME) { OM_uint32 minor; enumerateAttributes(&minor, peer, 1); } #endif return 0; }
/* * Function: server_establish_context * * Purpose: establishses a GSS-API context as a specified service with * an incoming client, and returns the context handle and associated * client name * * Arguments: * * s (r) an established TCP connection to the client * service_creds (r) server credentials, from gss_acquire_cred * context (w) the established GSS-API context * client_name (w) the client's ASCII name * * Returns: 0 on success, -1 on failure * * Effects: * * Any valid client request is accepted. If a context is established, * its handle is returned in context and the client name is returned * in client_name and 0 is returned. If unsuccessful, an error * message is displayed and -1 is returned. */ static int server_establish_context(int s, gss_cred_id_t server_creds, gss_ctx_id_t *context, gss_buffer_t client_name, OM_uint32 *ret_flags) { gss_buffer_desc send_tok, recv_tok; gss_name_t client; gss_OID doid; OM_uint32 maj_stat, min_stat, acc_sec_min_stat; gss_buffer_desc oid_name; int token_flags; if (recv_token(s, &token_flags, &recv_tok) < 0) return -1; if (recv_tok.value) { free(recv_tok.value); recv_tok.value = NULL; } if (!(token_flags & TOKEN_NOOP)) { if (logfile) fprintf(logfile, "Expected NOOP token, got %d token instead\n", token_flags); return -1; } *context = GSS_C_NO_CONTEXT; if (token_flags & TOKEN_CONTEXT_NEXT) { do { if (recv_token(s, &token_flags, &recv_tok) < 0) return -1; if (verbose && logfile) { fprintf(logfile, "Received token (size=%d): \n", (int) recv_tok.length); print_token(&recv_tok); } maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context, server_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &client, &doid, &send_tok, ret_flags, NULL, /* time_rec */ NULL); /* del_cred_handle */ if (recv_tok.value) { free(recv_tok.value); recv_tok.value = NULL; } if (send_tok.length != 0) { if (verbose && logfile) { fprintf(logfile, "Sending accept_sec_context token (size=%d):\n", (int) send_tok.length); print_token(&send_tok); } if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) { if (logfile) fprintf(logfile, "failure sending token\n"); return -1; } (void) gss_release_buffer(&min_stat, &send_tok); } if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { display_status("accepting context", maj_stat, acc_sec_min_stat); if (*context != GSS_C_NO_CONTEXT) gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER); return -1; } if (verbose && logfile) { if (maj_stat == GSS_S_CONTINUE_NEEDED) fprintf(logfile, "continue needed...\n"); else fprintf(logfile, "\n"); fflush(logfile); } } while (maj_stat == GSS_S_CONTINUE_NEEDED); /* display the flags */ display_ctx_flags(*ret_flags); if (verbose && logfile) { maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name); if (maj_stat != GSS_S_COMPLETE) { display_status("converting oid->string", maj_stat, min_stat); return -1; } fprintf(logfile, "Accepted connection using mechanism OID %.*s.\n", (int) oid_name.length, (char *) oid_name.value); (void) gss_release_buffer(&min_stat, &oid_name); } maj_stat = gss_display_name(&min_stat, client, client_name, &doid); if (maj_stat != GSS_S_COMPLETE) { display_status("displaying name", maj_stat, min_stat); return -1; } enumerateAttributes(&min_stat, client, TRUE); showLocalIdentity(&min_stat, client); maj_stat = gss_release_name(&min_stat, &client); if (maj_stat != GSS_S_COMPLETE) { display_status("releasing name", maj_stat, min_stat); return -1; } } else { client_name->length = *ret_flags = 0; if (logfile) fprintf(logfile, "Accepted unauthenticated connection.\n"); } return 0; }