size_t canl_io_write(canl_ctx cc, canl_io_handler io, void *buffer, size_t size, struct timeval *timeout) { io_handler *io_cc = (io_handler*) io; glb_ctx *glb_cc = (glb_ctx*) cc; int b_written = 0; struct canl_mech *mech; if (!cc) return -1; if (!io) { set_error(cc, EINVAL, POSIX_ERROR, "IO handler not initialized"); return -1; } if (io_cc->conn_ctx == NULL) return set_error(cc, EINVAL, POSIX_ERROR, "Connection not secured"); if (!buffer || !size) { set_error(cc, EINVAL, POSIX_ERROR, "No memory to read from"); return -1; } mech = find_mech(io_cc->oid); b_written = mech->write(glb_cc, io_cc, io_cc->conn_ctx, buffer, size, timeout); return b_written; }
/* close connection, preserve some info for the future reuse */ canl_err_code canl_io_close(canl_ctx cc, canl_io_handler io) { io_handler *io_cc = (io_handler*) io; glb_ctx *glb_cc = (glb_ctx*) cc; int err = 0; canl_mech *mech; /*check cc and io*/ if (!cc) { return EINVAL; /* XXX Should rather be a CANL error */ } if (!io) return set_error(cc, EINVAL, POSIX_ERROR, "IO handler not initialized"); if (io_cc->conn_ctx) { mech = find_mech(io_cc->oid); mech->close(glb_cc, io, io_cc->conn_ctx); /* XXX can it be safely reopened ?*/ } if (io_cc->sock != -1) { close (io_cc->sock); io_cc->sock = -1; } return err; }
/* return values: * 0 - success * 1 - failed, try another mechanism * -1 - failed, security extensions not supported */ int sec_login(const char *host, const char *mech_to_try) { int ret; struct sec_client_mech **m; void *tmp; /* shut up all messages this will produce (they are usually not very user friendly) */ ftp_set_tmp_verbosity(vbError); if(!mech_to_try || strcasecmp(mech_to_try, "none") == 0) return 0; m = find_mech(mech_to_try); if(!m) return 1; tmp = realloc(ftp->app_data, (*m)->size); if (tmp == NULL) { ftp_err(_("realloc %zu failed"), (*m)->size); return -1; } ftp->app_data = tmp; if ((*m)->init && (*(*m)->init) (ftp->app_data) != 0) { printf(_("Skipping %s...\n"), (*m)->name); return 1; } printf(_("Trying %s...\n"), (*m)->name); ret = ftp_cmd("AUTH %s", (*m)->name); if (ftp->code != ctContinue) { if (ret == 504) { printf(_("%s is not supported by the server.\n"), (*m)->name); } else if (ret == 534) { printf(_("%s rejected as security mechanism.\n"), (*m)->name); } else if (ftp->code == ctError) { printf(_("The server doesn't support the FTP " "security extensions.\n")); return -1; } return 1; } ret = (*(*m)->auth) (ftp->app_data, host); if (ret == AUTH_CONTINUE) return 1; else if (ret != AUTH_OK) { /* mechanism is supposed to output error string */ return -1; } ftp->mech = *m; ftp->sec_complete = 1; ftp->command_prot = prot_safe; return *m == NULL; }
/*TODO select + timeout, EINTR!!! */ canl_err_code canl_io_accept(canl_ctx cc, canl_io_handler io, int new_fd, struct sockaddr s_addr, int flags, canl_principal *peer, struct timeval *timeout) { int err = 0; io_handler *io_cc = (io_handler*) io; glb_ctx *glb_cc = (glb_ctx*) cc; struct canl_mech *mech = find_mech(GSS_C_NO_OID); void *conn_ctx = NULL; if (!glb_cc) return EINVAL; /* XXX Should rather be a CANL error */ if (!io_cc) return set_error(cc, EINVAL, POSIX_ERROR, "IO handler not initialized"); io_cc->sock = new_fd; err = mech->server_init(glb_cc, &conn_ctx); if (err) goto end; err = mech->accept(glb_cc, io_cc, conn_ctx, timeout); if (err) goto end; /* If peer != NULL then client certificate is mandatory*/ if (peer) { err = mech->get_peer(glb_cc, io_cc, conn_ctx, peer); if (err) goto end; } io_cc->conn_ctx = conn_ctx; io_cc->oid = GSS_C_NO_OID; err = 0; end: if (err) { (io_cc)->sock = -1; if (conn_ctx) mech->finish(glb_cc, conn_ctx); } return err; }
static void io_destroy(glb_ctx *cc, io_handler *io) { io_handler *io_cc = (io_handler*) io; canl_mech *mech; if (io == NULL) return; if (io_cc->conn_ctx) { mech = find_mech(io->oid); mech->finish(cc, io_cc->conn_ctx); io_cc->conn_ctx = NULL; io_cc->oid = GSS_C_NO_OID; } return; }
void canl_free_ctx(canl_ctx cc) { glb_ctx *ctx = (glb_ctx*) cc; struct canl_mech *mech = find_mech(GSS_C_NO_OID); if (!cc) return; /*delete content*/ if (ctx->err_msg) { free(ctx->err_msg); ctx->err_msg = NULL; } /*TODO delete ctx content for real*/ if (mech) mech->free_ctx(ctx); if (ctx->err_msg){ free(ctx->err_msg); ctx->err_msg = NULL; } free(ctx); }
static int accept_user(gss_ctx_id_t *ctx, gss_buffer_desc *in, gss_buffer_desc *out, gss_buffer_desc *name, char **pccname) { OM_uint32 maj_stat, min_stat; gss_name_t src_name = GSS_C_NO_NAME; gss_OID oid = GSS_C_NO_OID; int ret = -1; gss_cred_id_t delegated_cred_handle = NULL; *pccname = NULL; maj_stat = gss_accept_sec_context(&min_stat, ctx, GSS_C_NO_CREDENTIAL, in, GSS_C_NO_CHANNEL_BINDINGS, &src_name, &oid, out, NULL, NULL, &delegated_cred_handle); if ((maj_stat & GSS_S_CONTINUE_NEEDED) || maj_stat != GSS_S_COMPLETE) { fprintf(stderr, "gss_accept_sec_context: %08x %d %d ", maj_stat, maj_stat & GSS_S_CONTINUE_NEEDED, maj_stat != GSS_S_COMPLETE); gss_print_errors(min_stat); ret = HTTP_UNAUTHORIZED; goto out; } if (name) { /* Use display name */ maj_stat = gss_display_name(&min_stat, src_name, name, NULL); if (maj_stat != GSS_S_COMPLETE) { ret = HTTP_UNAUTHORIZED; goto out; } } ret = OK; if (delegated_cred_handle) { const struct mech_specific *m; m = find_mech(oid); if (m && m->save_cred) (*m->save_cred)(name->value, delegated_cred_handle, pccname); } else { /* fprintf(stderr, "Not delegated\r\n"); */ } out: if (src_name != GSS_C_NO_NAME) gss_release_name(&min_stat, &src_name); return ret; }
canl_err_code canl_io_connect(canl_ctx cc, canl_io_handler io, const char *host, const char *service, int port, gss_OID_set auth_mechs, int flags, canl_principal *peer, struct timeval *timeout) { int err = 0; io_handler *io_cc = (io_handler*) io; glb_ctx *glb_cc = (glb_ctx*) cc; struct _asyn_result ar; int i = 0, k; int addr_types[] = {AF_INET, AF_INET6}; //TODO ip versions policy? int ipver = AF_INET6; int j = 0, done; struct canl_mech *mech; gss_OID oid; memset(&ar, 0, sizeof(ar)); if (!glb_cc) { return EINVAL; } if (!io_cc) return set_error(glb_cc, EINVAL, POSIX_ERROR, "IO handler not initialized"); done = 0; for (k = 0; k < sizeof(addr_types)/sizeof(*addr_types); k++) { ipver = addr_types[k]; if (ar.ent) { free_hostent(ar.ent); memset(&ar, 0, sizeof(ar)); } ar.ent = (struct hostent *) calloc (1, sizeof(struct hostent)); if (ar.ent == NULL) return set_error(cc, ENOMEM, POSIX_ERROR, "Not enough memory"); switch (err = canl_asyn_getservbyname(ipver, &ar, host, NULL)) { case NETDB_SUCCESS: err = 0; break; case TRY_AGAIN: err = update_error(glb_cc, ETIMEDOUT, POSIX_ERROR, " Timeout reached when connecting to (%s)", host); goto end; case NETDB_INTERNAL: err = update_error(glb_cc, errno, POSIX_ERROR, "Cannot resolve the server hostname (%s)", host); continue; default: err = update_error(glb_cc, err, NETDB_ERROR, "Cannot resolve the server hostname (%s)", host); continue; } j = 0; do { if (auth_mechs == GSS_C_NO_OID_SET || auth_mechs->count == 0) oid = GSS_C_NO_OID; else oid = &auth_mechs->elements[j]; mech = find_mech(oid); err = 0; for (i = 0; ar.ent->h_addr_list[i]; i++) { void *ctx = NULL; if (err == ETIMEDOUT) goto end; err = try_connect(glb_cc, io_cc, ar.ent->h_addr_list[i], ar.ent->h_addrtype, port, timeout);//TODO timeout if (err) continue; err = mech->client_init(glb_cc, &ctx); if (err) { canl_io_close(glb_cc, io_cc); continue; } err = mech->connect(glb_cc, io_cc, ctx, timeout, host); if (err) { canl_io_close(glb_cc, io_cc); mech->finish(glb_cc, ctx); ctx = NULL; continue; } io_cc->conn_ctx = ctx; done = 1; /* If peer != NULL then client certificate is mandatory*/ if (peer) { err = mech->get_peer(glb_cc, io_cc, ctx, peer); if (err) goto end; } break; } if (err == ETIMEDOUT) goto end; j++; } while (auth_mechs != GSS_C_NO_OID_SET && j < auth_mechs->count && !done); free_hostent(ar.ent); ar.ent = NULL; if (done) break; } if (!done) { err = ECONNREFUSED; goto end; } err = 0; end: if (err) /* XXX: rather invent own error */ err = update_error(glb_cc, ECONNREFUSED, POSIX_ERROR, "Failed to make network connection to server %s", host); if (ar.ent != NULL) free_hostent(ar.ent); return err; }