void kexdh_client(Kex *kex) { BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; DH *dh = NULL; /* XXX: GCC */ Key *server_host_key; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf, *hash; u_int klen, slen, sbloblen, hashlen; int kout; /* generate and send 'e', client DH public key */ switch (kex->kex_type) { case KEX_DH_GRP1_SHA1: dh = dh_new_group1(); break; case KEX_DH_GRP14_SHA1: dh = dh_new_group14(); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } dh_gen_key(dh, kex->we_need * 8); packet_start(SSH2_MSG_KEXDH_INIT); packet_put_bignum2(dh->pub_key); packet_send(); debug("sending SSH2_MSG_KEXDH_INIT"); #ifdef DEBUG_KEXDH DHparams_print_fp(stderr, dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, dh->pub_key); fprintf(stderr, "\n"); #endif debug("expecting SSH2_MSG_KEXDH_REPLY"); packet_read_expect(SSH2_MSG_KEXDH_REPLY); /* key, cert */ server_host_key_blob = packet_get_string(&sbloblen); server_host_key = key_from_blob(server_host_key_blob, sbloblen); if (server_host_key == NULL) fatal("cannot decode server_host_key_blob"); if (server_host_key->type != kex->hostkey_type) fatal("type mismatch for decoded server_host_key_blob"); if (kex->verify_host_key == NULL) fatal("cannot verify server_host_key"); if (kex->verify_host_key(server_host_key) == -1) fatal("server_host_key verification failed"); /* DH parameter f, server public DH key */ if ((dh_server_pub = BN_new()) == NULL) fatal("dh_server_pub == NULL"); packet_get_bignum2(dh_server_pub); #ifdef DEBUG_KEXDH fprintf(stderr, "dh_server_pub= "); BN_print_fp(stderr, dh_server_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_server_pub)); #endif /* signed H */ signature = packet_get_string(&slen); packet_check_eom(); if (!dh_pub_is_valid(dh, dh_server_pub)) packet_disconnect("bad server public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); if ((kout = DH_compute_key(kbuf, dh_server_pub, dh)) < 0) fatal("DH_compute_key: failed"); #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif if ((shared_secret = BN_new()) == NULL) fatal("kexdh_client: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexdh_client: BN_bin2bn failed"); memset(kbuf, 0, klen); xfree(kbuf); /* calc and verify H */ kex_dh_hash( kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->my), buffer_len(&kex->my), buffer_ptr(&kex->peer), buffer_len(&kex->peer), server_host_key_blob, sbloblen, dh->pub_key, dh_server_pub, shared_secret, &hash, &hashlen ); xfree(server_host_key_blob); BN_clear_free(dh_server_pub); DH_free(dh); if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) fatal("key_verify failed for server_host_key"); key_free(server_host_key); xfree(signature); /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); }
/* * read packets, try to authenticate the user and * return only if authentication is successful */ static void do_authloop(Authctxt *authctxt) { int authenticated = 0; int prev = 0, type = 0; const struct AuthMethod1 *meth; debug("Attempting authentication for %s%.100s.", authctxt->valid ? "" : "invalid user ", authctxt->user); /* If the user has no password, accept authentication immediately. */ if (options.permit_empty_passwd && options.password_authentication && #ifdef KRB5 (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif PRIVSEP(auth_password(authctxt, ""))) { #ifdef USE_PAM if (options.use_pam && (PRIVSEP(do_pam_account()))) #endif { auth_log(authctxt, 1, 0, "without authentication", NULL); return; } } /* Indicate that authentication is needed. */ packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); for (;;) { /* default to fail */ authenticated = 0; /* Get a packet from the client. */ prev = type; type = packet_read(); /* * If we started challenge-response authentication but the * next packet is not a response to our challenge, release * the resources allocated by get_challenge() (which would * normally have been released by verify_response() had we * received such a response) */ if (prev == SSH_CMSG_AUTH_TIS && type != SSH_CMSG_AUTH_TIS_RESPONSE) abandon_challenge_response(authctxt); if (authctxt->failures >= options.max_authtries) goto skip; if ((meth = lookup_authmethod1(type)) == NULL) { logit("Unknown message during authentication: " "type %d", type); goto skip; } if (!*(meth->enabled)) { verbose("%s authentication disabled.", meth->name); goto skip; } authenticated = meth->method(authctxt); if (authenticated == -1) continue; /* "postponed" */ #ifdef BSD_AUTH if (authctxt->as) { auth_close(authctxt->as); authctxt->as = NULL; } #endif if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", authctxt->user); #ifdef _UNICOS if (authenticated && cray_access_denied(authctxt->user)) { authenticated = 0; fatal("Access denied for user %s.",authctxt->user); } #endif /* _UNICOS */ #ifndef HAVE_CYGWIN /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(meth->name)) { authenticated = 0; # ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); # endif } #endif #ifdef USE_PAM if (options.use_pam && authenticated && !PRIVSEP(do_pam_account())) { char *msg; size_t len; BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL); error("Access denied for user %s by PAM account " "configuration", authctxt->user); len = buffer_len(&loginmsg); buffer_append(&loginmsg, "\0", 1); msg = buffer_ptr(&loginmsg); /* strip trailing newlines */ if (len > 0) while (len > 0 && msg[--len] == '\n') msg[len] = '\0'; else msg = "Access denied."; packet_disconnect("%s", msg); } #endif skip: /* Log before sending the reply */ auth_log(authctxt, authenticated, 0, get_authname(type), NULL); free(client_user); client_user = NULL; if (authenticated) return; if (++authctxt->failures >= options.max_authtries) { #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); #endif auth_maxtries_exceeded(authctxt); } packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); } }
static void privsep_postauth(Authctxt *authctxt) { #ifdef DISABLE_FD_PASSING if (1) { #else if (authctxt->pw->pw_uid == 0 || options.use_login) { #endif /* File descriptor passing is broken or root login */ monitor_apply_keystate(pmonitor); use_privsep = 0; return; } /* Authentication complete */ alarm(0); if (startup_pipe != -1) { close(startup_pipe); startup_pipe = -1; } /* New socket pair */ monitor_reinit(pmonitor); pmonitor->m_pid = fork(); if (pmonitor->m_pid == -1) fatal("fork of unprivileged child failed"); else if (pmonitor->m_pid != 0) { debug2("User child is on pid %ld", (long)pmonitor->m_pid); close(pmonitor->m_recvfd); buffer_clear(&loginmsg); monitor_child_postauth(pmonitor); /* NEVERREACHED */ exit(0); } close(pmonitor->m_sendfd); /* Demote the private keys to public keys. */ demote_sensitive_data(); /* Drop privileges */ do_setusercontext(authctxt->pw); /* It is safe now to apply the key state */ monitor_apply_keystate(pmonitor); } static char * list_hostkey_types(void) { Buffer b; const char *p; char *ret; int i; buffer_init(&b); for (i = 0; i < options.num_host_key_files; i++) { Key *key = sensitive_data.host_keys[i]; if (key == NULL) continue; switch (key->type) { case KEY_RSA: case KEY_DSA: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); buffer_append(&b, p, strlen(p)); break; } } buffer_append(&b, "\0", 1); ret = xstrdup(buffer_ptr(&b)); buffer_free(&b); debug("list_hostkey_types: %s", ret); return ret; }
static int userauth_pubkey(Authctxt *authctxt) { Buffer b; Key *key = NULL; char *pkalg; u_char *pkblob, *sig; u_int alen, blen, slen; int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { debug2("userauth_pubkey: disabled because of invalid user"); return 0; } have_sig = packet_get_char(); if (datafellows & SSH_BUG_PKAUTH) { debug2("userauth_pubkey: SSH_BUG_PKAUTH"); /* no explicit pkalg given */ pkblob = packet_get_string(&blen); buffer_init(&b); buffer_append(&b, pkblob, blen); /* so we have to extract the pkalg from the pkblob */ pkalg = buffer_get_string(&b, &alen); buffer_free(&b); } else { pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); } pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("userauth_pubkey: unsupported public key algorithm: %s", pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("userauth_pubkey: cannot decode key: %s", pkalg); goto done; } if (key->type != pktype) { error("userauth_pubkey: type mismatch for decoded key " "(received %d, expected %d)", key->type, pktype); goto done; } if (have_sig) { sig = packet_get_string(&slen); packet_check_eom(); buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); } else { buffer_put_string(&b, session_id2, session_id2_len); } /* reconstruct packet */ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->user); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, "publickey"); buffer_put_char(&b, have_sig); buffer_put_cstring(&b, pkalg); } buffer_put_string(&b, pkblob, blen); #ifdef DEBUG_PK buffer_dump(&b); #endif /* test for correct signature */ authenticated = 0; if (PRIVSEP(user_key_allowed(authctxt->pw, key)) && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) authenticated = 1; buffer_free(&b); xfree(sig); } else { debug("test whether pkalg/pkblob are acceptable"); packet_check_eom(); /* XXX fake reply and always send PK_OK ? */ /* * XXX this allows testing whether a user is allowed * to login: if you happen to have a valid pubkey this * message is sent. the message is NEVER sent at all * if a user is not allowed to login. is this an * issue? -markus */ if (PRIVSEP(user_key_allowed(authctxt->pw, key))) { packet_start(SSH2_MSG_USERAUTH_PK_OK); packet_put_string(pkalg, alen); packet_put_string(pkblob, blen); packet_send(); packet_write_wait(); authctxt->postponed = 1; } } if (authenticated != 1) auth_clear_options(); done: debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); if (key != NULL) key_free(key); xfree(pkalg); xfree(pkblob); #ifdef HAVE_CYGWIN if (check_nt_auth(0, authctxt->pw) == 0) authenticated = 0; #endif return authenticated; }
void userauth_finish(Authctxt *authctxt, int authenticated, const char *method, const char *submethod) { char *methods; int partial = 0; if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", authctxt->user); if (authenticated && authctxt->postponed) fatal("INTERNAL ERROR: authenticated and postponed"); /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(method)) { authenticated = 0; #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); #endif } if (authenticated && options.num_auth_methods != 0) { if (!auth2_update_methods_lists(authctxt, method, submethod)) { authenticated = 0; partial = 1; } } /* Log before sending the reply */ auth_log(authctxt, authenticated, partial, method, submethod); if (authctxt->postponed) return; #ifdef USE_PAM if (options.use_pam && authenticated) { if (!PRIVSEP(do_pam_account())) { /* if PAM returned a message, send it to the user */ if (buffer_len(&loginmsg) > 0) { buffer_append(&loginmsg, "\0", 1); userauth_send_banner(buffer_ptr(&loginmsg)); packet_write_wait(); } fatal("Access denied for user %s by PAM account " "configuration", authctxt->user); } } #endif #ifdef _UNICOS if (authenticated && cray_access_denied(authctxt->user)) { authenticated = 0; fatal("Access denied for user %s.", authctxt->user); } #endif /* _UNICOS */ if (authenticated == 1) { /* turn off userauth */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); packet_start(SSH2_MSG_USERAUTH_SUCCESS); packet_send(); packet_write_wait(); /* now we can break out */ authctxt->success = 1; } else { /* Allow initial try of "none" auth without failure penalty */ if (!authctxt->server_caused_failure && (authctxt->attempt > 1 || strcmp(method, "none") != 0)) authctxt->failures++; if (authctxt->failures >= options.max_authtries) { #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); #endif packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } methods = authmethods_get(authctxt); debug3("%s: failure partial=%d next methods=\"%s\"", __func__, partial, methods); packet_start(SSH2_MSG_USERAUTH_FAILURE); packet_put_cstring(methods); packet_put_char(partial); packet_send(); packet_write_wait(); free(methods); } }
void filecache_update(TARGET *t) { MD5SUM blobmd5sum; int haveblobmd5sum = 0; const char *cachedname; const char *blobname; int cacheerror; if (!t->filecache_generate) return; /* If the buildmd5sum is empty, then the file doesn't exist. */ cacheerror = ismd5empty(t->buildmd5sum); if (cacheerror) return; haveblobmd5sum = 0; cachedname = filecache_getfilename(t, t->buildmd5sum, NULL); if (!cachedname) return; /* Search for the appropriate .link file that matches the target. */ haveblobmd5sum = filecache_findlink(cachedname, blobmd5sum); /* If we weren't able to determine the target md5sum, do it now. */ if (!haveblobmd5sum) { #ifdef OPT_BUILTIN_LUA_SUPPORT_EXT LIST *md5callback; pushsettings( t->settings ); md5callback = var_get( "MD5CALLBACK" ); popsettings( t->settings ); if ( list_first(md5callback) ) { luahelper_md5callback(t->boundname, blobmd5sum, list_value(list_first(md5callback))); } else { #endif md5file(t->boundname, blobmd5sum); #ifdef OPT_BUILTIN_LUA_SUPPORT_EXT } #endif memcpy(t->contentmd5sum, blobmd5sum, sizeof(MD5SUM)); if (ismd5empty(t->contentmd5sum)) return; } { /* Is the blob already there? */ time_t blobtime; blobname = filecache_getfilename(t, blobmd5sum, ".blob"); if (file_time(blobname, &blobtime) == -1) { time_t blobpartialtime; const char *blobpartialname; if(DEBUG_MD5HASH) printf("Caching %s as %s\n", t->name, cachedname); else printf("Caching %s\n", t->name); /* Write the new .blob to the cache. */ blobpartialname = filecache_getfilename(t, blobmd5sum, ".blob.partial"); if (file_time(blobpartialname, &blobpartialtime) == -1) { if (copyfile(blobpartialname, t->boundname, &blobmd5sum) == 0 || rename(blobpartialname, blobname) != 0) { printf("** Unable to write %s to cache.\n", t->name); filecache_disable(t); return; } } } } /* Write the new .link file to the cache. */ { FILE *file; BUFFER linknamebuff; buffer_init(&linknamebuff); buffer_addstring(&linknamebuff, cachedname, strlen(cachedname)); buffer_addchar(&linknamebuff, '-'); buffer_addstring(&linknamebuff, md5tostring(blobmd5sum), 32); buffer_addstring(&linknamebuff, ".link", 5); buffer_addchar(&linknamebuff, 0); file_mkdir(buffer_ptr(&linknamebuff)); file = fopen(buffer_ptr(&linknamebuff), "wb"); if (file) { write_md5sum(file, blobmd5sum); write_string(file, t->name); fclose(file); } buffer_free(&linknamebuff); } }
void kexdh_server(Kex *kex) { BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; DH *dh; Key *server_host_key; u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; u_int sbloblen, klen, kout, hashlen; u_int slen; /* generate server DH public key */ switch (kex->kex_type) { case KEX_DH_GRP1_SHA1: dh = dh_new_group1(); break; case KEX_DH_GRP14_SHA1: dh = dh_new_group14(); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } dh_gen_key(dh, kex->we_need * 8); debug("expecting SSH2_MSG_KEXDH_INIT"); packet_read_expect(SSH2_MSG_KEXDH_INIT); if (kex->load_host_key == NULL) fatal("Cannot load hostkey"); server_host_key = kex->load_host_key(kex->hostkey_type); if (server_host_key == NULL) fatal("Unsupported hostkey type %d", kex->hostkey_type); /* key, cert */ if ((dh_client_pub = BN_new()) == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub); packet_check_eom(); #ifdef DEBUG_KEXDH fprintf(stderr, "dh_client_pub= "); BN_print_fp(stderr, dh_client_pub); fprintf(stderr, "\n"); debug("bits %d", BN_num_bits(dh_client_pub)); #endif #ifdef DEBUG_KEXDH DHparams_print_fp(stderr, dh); fprintf(stderr, "pub= "); BN_print_fp(stderr, dh->pub_key); fprintf(stderr, "\n"); #endif if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_client_pub, dh); #ifdef DEBUG_KEXDH dump_digest("shared secret", kbuf, kout); #endif if ((shared_secret = BN_new()) == NULL) fatal("kexdh_server: BN_new failed"); BN_bin2bn(kbuf, kout, shared_secret); memset(kbuf, 0, klen); xfree(kbuf); key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); /* calc H */ kex_dh_hash( kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->peer), buffer_len(&kex->peer), buffer_ptr(&kex->my), buffer_len(&kex->my), server_host_key_blob, sbloblen, dh_client_pub, dh->pub_key, shared_secret, &hash, &hashlen ); BN_clear_free(dh_client_pub); /* save session id := H */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } /* sign H */ PRIVSEP(key_sign(server_host_key, &signature, &slen, hash, hashlen)); /* destroy_sensitive_data(); */ /* send server hostkey, DH pubkey 'f' and singed H */ packet_start(SSH2_MSG_KEXDH_REPLY); packet_put_string(server_host_key_blob, sbloblen); packet_put_bignum2(dh->pub_key); /* f */ packet_put_string(signature, slen); packet_send(); xfree(signature); xfree(server_host_key_blob); /* have keys, free DH */ DH_free(dh); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); }
int mm_answer_pty(int sock, Buffer *m) { extern struct monitor *pmonitor; Session *s; int res, fd0; debug3("%s entering", __func__); buffer_clear(m); s = session_new(); if (s == NULL) goto error; s->authctxt = authctxt; s->pw = authctxt->pw; s->pid = pmonitor->m_pid; res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); if (res == 0) goto error; pty_setowner(authctxt->pw, s->tty); buffer_put_int(m, 1); buffer_put_cstring(m, s->tty); /* We need to trick ttyslot */ if (dup2(s->ttyfd, 0) == -1) fatal("%s: dup2", __func__); mm_record_login(s, authctxt->pw); /* Now we can close the file descriptor again */ close(0); /* send messages generated by record_login */ buffer_put_string(m, buffer_ptr(&loginmsg), buffer_len(&loginmsg)); buffer_clear(&loginmsg); mm_request_send(sock, MONITOR_ANS_PTY, m); if (mm_send_fd(sock, s->ptyfd) == -1 || mm_send_fd(sock, s->ttyfd) == -1) fatal("%s: send fds failed", __func__); /* make sure nothing uses fd 0 */ if ((fd0 = open(_PATH_DEVNULL, O_RDONLY)) < 0) fatal("%s: open(/dev/null): %s", __func__, strerror(errno)); if (fd0 != 0) error("%s: fd0 %d != 0", __func__, fd0); /* slave is not needed */ close(s->ttyfd); s->ttyfd = s->ptyfd; /* no need to dup() because nobody closes ptyfd */ s->ptymaster = s->ptyfd; debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ttyfd); return (0); error: if (s != NULL) mm_session_close(s); buffer_put_int(m, 0); mm_request_send(sock, MONITOR_ANS_PTY, m); return (0); }
LIST * var_expand( LIST *prefix, const char *in, const char *end, LOL *lol, int cancopyin ) { BUFFER buff; const char *inp = in; int depth; size_t save_buffer_pos, ov_save_buffer_pos; int literal = 0; if( DEBUG_VAREXP ) printf( "expand '%.*s'\n", end - in, in ); /* This gets alot of cases: $(<) and $(>) */ if( end - in == 4 && in[0] == '$' && in[1] == leftParen && in[3] == rightParen ) { switch( in[2] ) { case '1': case '<': return list_copy( prefix, lol_get( lol, 0 ) ); case '2': case '>': return list_copy( prefix, lol_get( lol, 1 ) ); } } buffer_init( &buff ); /* Just try simple copy of in to out. */ while( in < end ) { char ch = *in++; buffer_addchar( &buff, ch ); if( ch == '$' && *in == leftParen ) goto expand; #ifdef OPT_EXPAND_LITERALS_EXT if( ch == '@' && *in == leftParen ) { literal = 1; goto expand; } if( ch == '@' && in[0] == '$' && in[1] == leftParen ) { ++in; literal = 1; goto expand; } #endif } /* No variables expanded - just add copy of input string to list. */ /* Cancopyin is an optimization: if the input was already a list */ /* item, we can use the copystr() to put it on the new list. */ /* Otherwise, we use the slower newstr(). */ buffer_putchar( &buff, 0 ); if( cancopyin ) { LIST *new_list = list_append( prefix, inp, 1 ); buffer_free( &buff ); return new_list; } else { LIST *new_list = list_append( prefix, buffer_ptr( &buff ), 0 ); buffer_free( &buff ); return new_list; } expand: /* * Input so far (ignore blanks): * * stuff-in-outbuf $(variable) remainder * ^ ^ * in end * Output so far: * * stuff-in-outbuf $ * ^ ^ * out_buf out * * * We just copied the $ of $(...), so back up one on the output. * We now find the matching close paren, copying the variable and * modifiers between the $( and ) temporarily into out_buf, so that * we can replace :'s with MAGIC_COLON. This is necessary to avoid * being confused by modifier values that are variables containing * :'s. Ugly. */ depth = 1; buffer_deltapos( &buff, -1 ); save_buffer_pos = buffer_pos( &buff ); in++; while( in < end && depth ) { char ch = *in++; buffer_addchar( &buff, ch ); if ( ch == leftParen ) { depth++; } else if ( ch == rightParen ) { depth--; } else { switch( ch ) { case ':': buffer_deltapos( &buff, -1 ); buffer_addchar( &buff, MAGIC_COLON ); break; case '[': buffer_deltapos( &buff, -1 ); buffer_addchar( &buff, MAGIC_LEFT ); break; case ']': buffer_deltapos( &buff, -1 ); buffer_addchar( &buff, MAGIC_RIGHT ); break; } } } /* Copied ) - back up. */ buffer_deltapos( &buff, -1 ); ov_save_buffer_pos = buffer_pos( &buff ); buffer_setpos( &buff, save_buffer_pos ); /* * Input so far (ignore blanks): * * stuff-in-outbuf $(variable) remainder * ^ ^ * in end * Output so far: * * stuff-in-outbuf variable * ^ ^ ^ * out_buf out ov * * Later we will overwrite 'variable' in out_buf, but we'll be * done with it by then. 'variable' may be a multi-element list, * so may each value for '$(variable element)', and so may 'remainder'. * Thus we produce a product of three lists. */ { LIST *variables = 0; LIST *remainder = 0; LISTITEM *vars; /* Recursively expand variable name & rest of input */ if( save_buffer_pos < ov_save_buffer_pos ) variables = var_expand( L0, buffer_posptr( &buff ), buffer_ptr( &buff ) + ov_save_buffer_pos, lol, 0 ); if( in < end ) remainder = var_expand( L0, in, end, lol, 0 ); /* Now produce the result chain */ /* For each variable name */ for( vars = list_first(variables); vars; vars = list_next( vars ) ) { LIST *value, *evalue = 0; LISTITEM* valueSliceStart = NULL; #ifdef OPT_EXPAND_LITERALS_EXT LIST *origvalue = 0; #endif char *colon; char *bracket; BUFFER varnamebuff; int sub1 = 0, sub2 = -1; VAR_EDITS edits; memset(&edits, 0, sizeof(VAR_EDITS)); if (leftParen == '{') { edits.empty.ptr = ""; edits.empty.len = 0; } /* Look for a : modifier in the variable name */ /* Must copy into varname so we can modify it */ buffer_init( &varnamebuff ); buffer_addstring( &varnamebuff, list_value(vars), strlen( list_value(vars) ) ); buffer_addchar( &varnamebuff, 0 ); if( ( colon = strchr( buffer_ptr( &varnamebuff ), MAGIC_COLON ) ) ) { *colon = '\0'; var_edit_parse( colon + 1, &edits ); } /* Look for [x-y] and [x-] subscripting */ /* sub1 is x (0 default) */ /* sub2 is length (-1 means forever) */ if( ( bracket = strchr( buffer_ptr( &varnamebuff ), MAGIC_LEFT ) ) ) { char *dash; if( ( dash = strchr( bracket + 1, '-' ) ) ) *dash = '\0'; sub1 = atoi( bracket + 1 ) - 1; if( !dash ) sub2 = 1; else if( !dash[1] || dash[1] == MAGIC_RIGHT ) sub2 = -1; else sub2 = atoi( dash + 1 ) - sub1; *bracket = '\0'; } /* Get variable value, specially handling $(<), $(>), $(n) */ #ifdef OPT_EXPAND_LITERALS_EXT if ( !literal ) #endif { const char* varname = buffer_ptr( &varnamebuff ); if( varname[0] == '<' && !varname[1] ) value = lol_get( lol, 0 ); else if( varname[0] == '>' && !varname[1] ) value = lol_get( lol, 1 ); else if( varname[0] >= '1' && varname[0] <= '9' && !varname[1] ) value = lol_get( lol, varname[0] - '1' ); else if ( edits.targetsetting ) { TARGET* t = bindtarget(edits.targetname.ptr); SETTINGS* settings = quicksettingslookup(t, varname); if (settings) value = list_copy(L0, settings->value); else value = L0; } else value = var_get( varname ); } #ifdef OPT_EXPAND_LITERALS_EXT else { origvalue = value = list_append( L0, buffer_ptr( &varnamebuff ), 0 ); } #endif /* The fast path: $(x) - just copy the variable value. */ /* This is only an optimization */ if( buffer_isempty( &buff ) && !bracket && !colon && in == end ) { prefix = list_copy( prefix, value ); buffer_free( &buff ); continue; } /* Handle start subscript */ valueSliceStart = list_first(value); while(sub1 > 0 && valueSliceStart) { sub1 -= 1; valueSliceStart = list_next(valueSliceStart); } /* Empty w/ :E=default? */ if( !valueSliceStart && (colon || leftParen == '{') && edits.empty.ptr ) { evalue = value = list_append( L0, edits.empty.ptr, 0 ); valueSliceStart = list_first(value); } #ifdef OPT_EXPAND_LITERALS_EXT if ( colon && edits.expandliteral ) { LOL lol; char const* string = list_value(list_first(value)); LIST *newvalue = var_expand( L0, string, string + strlen( string ), &lol, 0 ); if ( origvalue ) { list_free( origvalue ); origvalue = 0; } value = newvalue; valueSliceStart = list_first(value); sub2 = -1; } #endif #ifdef OPT_EXPAND_FILEGLOB_EXT if ( edits.wildcard ) { LIST *newl = L0; for( ; valueSliceStart; valueSliceStart = list_next( valueSliceStart ) ) { LIST *foundfiles = L0; fileglob* glob; /* Handle end subscript (length actually) */ if( sub2 >= 0 && --sub2 < 0 ) break; glob = fileglob_Create( list_value(valueSliceStart) ); while ( fileglob_Next( glob ) ) { foundfiles = list_append( foundfiles, fileglob_FileName( glob ) + edits.wildcard_remove_prepend.len, 0 ); } fileglob_Destroy( glob ); /* TODO: Efficiency: Just append to newl above? */ newl = list_copy( newl, foundfiles ); list_free( foundfiles ); } if ( origvalue ) { list_free( origvalue ); origvalue = 0; } value = newl; origvalue = value; valueSliceStart = list_first(value); } #endif /* For each variable value */ for( ; valueSliceStart; valueSliceStart = list_next( valueSliceStart ) ) { LISTITEM *rem; size_t save_buffer_pos; size_t end_buffer_pos; const char *valuestring; /* Handle end subscript (length actually) */ if( sub2 >= 0 && --sub2 < 0 ) break; /* Apply : mods, if present */ save_buffer_pos = buffer_pos( &buff ); valuestring = list_value(valueSliceStart); #ifdef OPT_EXPAND_BINDING_EXT if( colon && edits.expandbinding ) { SETTINGS *expandText; TARGET *t = bindtarget( valuestring ); expandText = quicksettingslookup( t, "EXPAND_TEXT" ); if ( expandText && list_first(expandText->value) ) { valuestring = list_value(list_first(expandText->value)); } else { if( t->binding == T_BIND_UNBOUND ) { t->boundname = search_using_target_settings( t, t->name, &t->time ); t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING; } valuestring = t->boundname; } } #endif if( colon && edits.filemods ) { var_edit_file( valuestring, &buff, &edits ); } else { buffer_addstring( &buff, valuestring, strlen( valuestring ) + 1 ); } buffer_setpos( &buff, save_buffer_pos ); if( colon && ( edits.upshift || edits.downshift ) ) var_edit_shift( buffer_posptr( &buff ), &edits ); #ifdef OPT_SLASH_MODIFIERS_EXT if( colon && ( edits.fslash || edits.bslash ) ) var_edit_slash( buffer_posptr( &buff ), &edits ); #endif #ifdef OPT_EXPAND_ESCAPE_PATH_EXT if ( colon && edits.escapepath ) { const char* ptr = buffer_posptr( &buff ); const char* endptr = ptr + strlen( ptr ); BUFFER escapebuff; buffer_init( &escapebuff ); save_buffer_pos = buffer_pos( &buff ); #ifdef NT while ( ptr != endptr && *ptr != ' ' && *ptr != '/' ) ++ptr; if (*ptr == ' ' || *ptr == '/' ) { buffer_addchar( &escapebuff, '"' ); buffer_addstring( &escapebuff, buffer_posptr( &buff ), endptr - buffer_posptr( &buff ) ); buffer_addchar( &escapebuff, '"' ); buffer_addchar( &escapebuff, 0 ); buffer_addstring( &buff, buffer_ptr( &escapebuff ), buffer_pos( &escapebuff ) ); } #else while ( ptr != endptr ) { if ( *ptr == ' ' || *ptr == '\\' || *ptr == leftParen || *ptr == rightParen || *ptr == '"' ) { buffer_addchar( &escapebuff, '\\' ); } buffer_addchar( &escapebuff, *ptr ); ++ptr; } buffer_addchar( &escapebuff, 0 ); buffer_addstring( &buff, buffer_ptr( &escapebuff ), buffer_pos( &escapebuff ) ); #endif buffer_setpos( &buff, save_buffer_pos ); buffer_free( &escapebuff ); } #endif /* Handle :J=joinval */ /* If we have more values for this var, just */ /* keep appending them (with the join value) */ /* rather than creating separate LIST elements. */ if( colon && edits.join.ptr && ( list_next( valueSliceStart ) || list_next( vars ) ) ) { buffer_setpos( &buff, buffer_pos( &buff ) + strlen( buffer_posptr( &buff ) ) ); buffer_addstring( &buff, edits.join.ptr, strlen( edits.join.ptr ) + 1 ); buffer_deltapos( &buff, -1 ); continue; } /* If no remainder, append result to output chain. */ if( in == end ) { prefix = list_append( prefix, buffer_ptr( &buff ), 0 ); continue; } /* For each remainder, append the complete string */ /* to the output chain. */ /* Remember the end of the variable expansion so */ /* we can just tack on each instance of 'remainder' */ save_buffer_pos = buffer_pos( &buff ); end_buffer_pos = strlen( buffer_ptr( &buff ) ); buffer_setpos( &buff, end_buffer_pos ); for( rem = list_first(remainder); rem; rem = list_next( rem ) ) { buffer_addstring( &buff, list_value(rem), strlen( list_value(rem) ) + 1 ); buffer_setpos( &buff, end_buffer_pos ); prefix = list_append( prefix, buffer_ptr( &buff ), 0 ); } buffer_setpos( &buff, save_buffer_pos ); } /* Toss used empty */ if( evalue ) list_free( evalue ); #ifdef OPT_EXPAND_LITERALS_EXT if ( origvalue ) list_free( origvalue ); #endif #ifdef OPT_EXPAND_INCLUDES_EXCLUDES_EXT if ( edits.includes_excludes ) { LIST *newl = L0; LISTITEM* l; LIST *origprefix = prefix; int hasInclude = 0; if ( !regexhash ) regexhash = hashinit( sizeof(regexdata), "regex" ); { LISTITEM *inex = list_first(edits.includes_excludes); while ( inex ) { char mod = list_value(inex)[0]; inex = list_next( inex ); if ( mod == 'I' ) { hasInclude = 1; } } } for (l = list_first(prefix) ; l; l = list_next( l ) ) { LISTITEM *inex = list_first(edits.includes_excludes); int remove = hasInclude; while ( inex ) { char mod = list_value(inex)[0]; regexp *re; regexdata data, *d = &data; inex = list_next( inex ); data.name = list_value(inex); if( !hashcheck( regexhash, (HASHDATA **)&d ) ) { d->re = jam_regcomp( list_value(inex) ); (void)hashenter( regexhash, (HASHDATA **)&d ); } re = d->re; inex = list_next( inex ); if ( mod == 'X' ) { if( jam_regexec( re, list_value(l) ) ) remove = 1; } else if ( mod == 'I' ) { if( jam_regexec( re, list_value(l) ) ) remove = 0; } } if ( !remove ) newl = list_append( newl, list_value(l), 1 ); } /* TODO: Efficiency: Just modify prefix? */ list_free( origprefix ); prefix = newl; } #endif //#ifdef OPT_EXPAND_LITERALS_EXT // buffer_free( &buff ); //#endif #ifdef OPT_EXPAND_INCLUDES_EXCLUDES_EXT list_free( edits.includes_excludes ); #endif } /* variables & remainder were gifts from var_expand */ /* and must be freed */ list_free( variables ); list_free( remainder ); if( DEBUG_VAREXP ) { printf( "expanded to " ); list_print( prefix ); printf( "\n" ); } buffer_free( &buff ); return prefix; } }
int userauth_gssapi_keyex(Authctxt *authctxt) { Gssctxt *gssctxt; gss_buffer_desc send_tok; OM_uint32 status; static int attempt = 0; if (authctxt == NULL || authctxt->method == NULL) fatal("input_gssapi_response: no authentication context"); if (xxx_gssctxt == NULL || xxx_gssctxt->context == GSS_C_NO_CONTEXT) return 0; if (strcmp(authctxt->method->name, "gssapi-keyex") == 0) authctxt->methoddata = gssctxt = xxx_gssctxt; if (attempt++ >= 1) return 0; if (strcmp(authctxt->method->name, "gssapi-keyex") == 0) { gss_buffer_desc g_mic_data; Buffer mic_data; debug2("Authenticating with GSS-API context from key exchange (w/ MIC)"); /* Make data buffer to MIC */ buffer_init(&mic_data); buffer_put_string(&mic_data, session_id2, session_id2_len); buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&mic_data, authctxt->server_user); buffer_put_cstring(&mic_data, authctxt->service); buffer_put_cstring(&mic_data, authctxt->method->name); /* Make MIC */ g_mic_data.value = buffer_ptr(&mic_data); g_mic_data.length = buffer_len(&mic_data); status = ssh_gssapi_get_mic(gssctxt, &g_mic_data, &send_tok); buffer_clear(&mic_data); if (GSS_ERROR(status) || send_tok.length == 0) { /* * Oops, now what? There's no error token... * Next userauth */ debug("GSS_GetMIC() failed! - " "Abandoning GSSAPI userauth"); clear_auth_state(authctxt); userauth(authctxt,NULL); return 0; } packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_string(send_tok.value,send_tok.length); /* MIC */ packet_send(); packet_write_wait(); (void) gss_release_buffer(&status, &send_tok); } else if (strcmp(authctxt->method->name, "external-keyx") == 0) { debug2("Authentication with deprecated \"external-keyx\"" " method not supported"); return 0; } return 1; }
void mm_send_keystate(struct monitor *monitor) { Buffer m, *input, *output; u_char *blob, *p; u_int bloblen, plen; u_int32_t seqnr, packets; u_int64_t blocks, bytes; buffer_init(&m); if (!compat20) { u_char iv[24]; u_char *key; u_int ivlen, keylen; buffer_put_int(&m, packet_get_protocol_flags()); buffer_put_int(&m, packet_get_ssh1_cipher()); debug3("%s: Sending ssh1 KEY+IV", __func__); keylen = packet_get_encryption_key(NULL); key = xmalloc(keylen+1); /* add 1 if keylen == 0 */ keylen = packet_get_encryption_key(key); buffer_put_string(&m, key, keylen); explicit_bzero(key, keylen); free(key); ivlen = packet_get_keyiv_len(MODE_OUT); packet_get_keyiv(MODE_OUT, iv, ivlen); buffer_put_string(&m, iv, ivlen); ivlen = packet_get_keyiv_len(MODE_IN); packet_get_keyiv(MODE_IN, iv, ivlen); buffer_put_string(&m, iv, ivlen); goto skip; } else { /* Kex for rekeying */ mm_send_kex(&m, *monitor->m_pkex); } debug3("%s: Sending new keys: %p %p", __func__, packet_get_newkeys(MODE_OUT), packet_get_newkeys(MODE_IN)); /* Keys from Kex */ if (!mm_newkeys_to_blob(MODE_OUT, &blob, &bloblen)) fatal("%s: conversion of newkeys failed", __func__); buffer_put_string(&m, blob, bloblen); free(blob); if (!mm_newkeys_to_blob(MODE_IN, &blob, &bloblen)) fatal("%s: conversion of newkeys failed", __func__); buffer_put_string(&m, blob, bloblen); free(blob); packet_get_state(MODE_OUT, &seqnr, &blocks, &packets, &bytes); buffer_put_int(&m, seqnr); buffer_put_int64(&m, blocks); buffer_put_int(&m, packets); buffer_put_int64(&m, bytes); packet_get_state(MODE_IN, &seqnr, &blocks, &packets, &bytes); buffer_put_int(&m, seqnr); buffer_put_int64(&m, blocks); buffer_put_int(&m, packets); buffer_put_int64(&m, bytes); debug3("%s: New keys have been sent", __func__); skip: /* More key context */ plen = packet_get_keycontext(MODE_OUT, NULL); p = xmalloc(plen+1); packet_get_keycontext(MODE_OUT, p); buffer_put_string(&m, p, plen); free(p); plen = packet_get_keycontext(MODE_IN, NULL); p = xmalloc(plen+1); packet_get_keycontext(MODE_IN, p); buffer_put_string(&m, p, plen); free(p); /* Compression state */ debug3("%s: Sending compression state", __func__); buffer_put_string(&m, &outgoing_stream, sizeof(outgoing_stream)); buffer_put_string(&m, &incoming_stream, sizeof(incoming_stream)); /* Network I/O buffers */ input = (Buffer *)packet_get_input(); output = (Buffer *)packet_get_output(); buffer_put_string(&m, buffer_ptr(input), buffer_len(input)); buffer_put_string(&m, buffer_ptr(output), buffer_len(output)); /* Roaming */ if (compat20) { buffer_put_int64(&m, get_sent_bytes()); buffer_put_int64(&m, get_recv_bytes()); } mm_request_send(monitor->m_recvfd, MONITOR_REQ_KEYEXPORT, &m); debug3("%s: Finished sending state", __func__); buffer_free(&m); }
void input_gssapi_token(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok, recv_tok, g_mic_data; Buffer mic_data; OM_uint32 status; u_int slen; if (authctxt == NULL || authctxt->method == NULL) fatal("input_gssapi_response: no authentication context"); gssctxt = authctxt->methoddata; recv_tok.value=packet_get_string(&slen); recv_tok.length=slen; /* safe typecast */ status=ssh_gssapi_init_ctx(gssctxt, authctxt->host, options.gss_deleg_creds, &recv_tok, &send_tok); packet_check_eom(); if (GSS_ERROR(status)) { if (send_tok.length>0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); packet_put_string(send_tok.value,send_tok.length); packet_send(); packet_write_wait(); } /* Start again with the next method in the list */ clear_auth_state(authctxt); userauth(authctxt,NULL); return; } if (send_tok.length>0) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); packet_put_string(send_tok.value,send_tok.length); packet_send(); packet_write_wait(); } if (status != GSS_S_COMPLETE) return; /* Make data buffer to MIC */ buffer_init(&mic_data); buffer_put_string(&mic_data, session_id2, session_id2_len); buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&mic_data, authctxt->server_user); buffer_put_cstring(&mic_data, authctxt->service); buffer_put_cstring(&mic_data, authctxt->method->name); /* Make MIC */ g_mic_data.value = buffer_ptr(&mic_data); g_mic_data.length = buffer_len(&mic_data); status = ssh_gssapi_get_mic(gssctxt, &g_mic_data, &send_tok); buffer_clear(&mic_data); if (GSS_ERROR(status) || send_tok.length == 0) { /* * Oops, now what? There's no error token... * Next userauth */ debug("GSS_GetMIC() failed! - " "Abandoning GSSAPI userauth"); clear_auth_state(authctxt); userauth(authctxt,NULL); return; } packet_start(SSH2_MSG_USERAUTH_GSSAPI_MIC); packet_put_string(send_tok.value,send_tok.length); packet_send(); packet_write_wait(); }
static int sign_and_send_pubkey(Authctxt *authctxt, Key *k, sign_cb_fn *sign_callback) { Buffer b; u_char *blob, *signature; u_int bloblen, slen; int skip = 0; int ret = -1; int have_sig = 1; debug3("sign_and_send_pubkey"); if (key_to_blob(k, &blob, &bloblen) == 0) { /* we cannot handle this key */ debug3("sign_and_send_pubkey: cannot handle key"); return 0; } /* data to be signed */ buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); skip = session_id2_len; } else { buffer_put_string(&b, session_id2, session_id2_len); skip = buffer_len(&b); } buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); buffer_put_cstring(&b, key_ssh_name(k)); } buffer_put_string(&b, blob, bloblen); /* generate signature */ ret = (*sign_callback)(authctxt, k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); if (ret == -1) { xfree(blob); buffer_free(&b); return 0; } #ifdef DEBUG_PK buffer_dump(&b); #endif if (datafellows & SSH_BUG_PKSERVICE) { buffer_clear(&b); buffer_append(&b, session_id2, session_id2_len); skip = session_id2_len; buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, authctxt->service); buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); if (!(datafellows & SSH_BUG_PKAUTH)) buffer_put_cstring(&b, key_ssh_name(k)); buffer_put_string(&b, blob, bloblen); } xfree(blob); /* append signature */ buffer_put_string(&b, signature, slen); xfree(signature); /* skip session id and packet type */ if (buffer_len(&b) < skip + 1) fatal("userauth_pubkey: internal error"); buffer_consume(&b, skip + 1); /* put remaining data from buffer into packet */ packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_raw(buffer_ptr(&b), buffer_len(&b)); buffer_free(&b); packet_send(); return 1; }
/** Add a DSA key to the tspc key file * * @param dsa the DSA param pointer filled with our key info * @param host the hostname of the corresponding broker * @param filename the keyfile to use * * @return 0 if error * 1 if ok * */ int add_dsakey_to_keyfile(DSA *dsa, char *host, char *filename, tBoolean autoaccept) { FILE *fp = NULL; Buffer buf; char *str = NULL; int ret = 0; switch (is_dsakey_in_keyfile(dsa, host, filename)) { case 0: Display(LOG_LEVEL_3, ELInfo, TSP_AUTH_PASSDSS_STRING, GOGO_STR_ERR_IN_KEY_VERIF); Display(LOG_LEVEL_3, ELWarning, TSP_AUTH_PASSDSS_STRING, GOGO_STR_SERVER_KEY_REJECTED); break; case 1: /* not in, we add and continue */ #if defined(WIN32) && !defined(WINCE) // When running as a service we can't ask user // permission. Compromise and accept the key auto // if (!IsService && !autoaccept) { #else if (!autoaccept) { #endif if (!ask(GOGO_STR_UNKNOWN_HOST_ADD_KEY, host)) { Display(LOG_LEVEL_3, ELWarning, TSP_AUTH_PASSDSS_STRING, GOGO_STR_SERVER_KEY_REJECTED_USER); break; } } else Display(LOG_LEVEL_1, ELWarning, TSP_AUTH_PASSDSS_STRING, GOGO_STR_WARN_SERVER_KEY_AUTO_ADDED); Display(LOG_LEVEL_2, ELInfo, TSP_AUTH_PASSDSS_STRING, GOGO_STR_SERVER_KEY_ACCEPTED_ADDED); buffer_init(&buf); if (buf.buf == NULL) break; buffer_put_cstring(&buf, "ssh-dss"); buffer_put_bignum(&buf, dsa->p); buffer_put_bignum(&buf, dsa->q); buffer_put_bignum(&buf, dsa->g); buffer_put_bignum(&buf, dsa->pub_key); if ( (str = pal_malloc(2 * buffer_len(&buf))) == NULL) break; if ( (base64encode(str, buffer_ptr(&buf), (int) buffer_len(&buf))) < 1) break; fp = fopen(filename, "a"); if (fp) { fprintf(fp, "%s ssh-dss %s\n", host, str); fclose(fp); ret = 1; } buffer_free(&buf); pal_free(str); break; case 2: /* in and matching correctly, hurray */ Display(LOG_LEVEL_2, ELInfo, TSP_AUTH_PASSDSS_STRING, GOGO_STR_MATCHING_KEY_FOUND_USED); ret = 1; break; case 3: /* in and NOT matching correctly */ Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, GOGO_STR_WARN_STORED_LOCAL_KEY_NO_MATCH, filename, host); Display(LOG_LEVEL_3, ELWarning, TSP_AUTH_PASSDSS_STRING, GOGO_STR_SERVER_KEY_REJECTED); ret = 0; break; } return ret; } /** * Authenticate to the Migration Broker using PASSDSS-3DES-1 * * Buf_H will contain the data used to validate the server * signature. The data is a concatenation of the following parameters, * in that order: * azname,authname,DH_public_key,pklength,"ssh-dss",p,q,g,z,Y,ssecmask,sbuflen,dh_K * * @param socket * @param user * @param passwd * @param host * @param nt * * @return * * @todo DH public key validation (RFC2631, 2.1.5) * @todo Local storage for server public keys * */ gogoc_status AuthPASSDSS_3DES_1(pal_socket_t socket, net_tools_t *nt, tConf *conf, tBrokerList **broker_list) { DH *dh = NULL; /**< client DH key used to exchange key with server */ DSA *dsa = NULL; /**< Remote server DSA key public information */ DSA_SIG *sig = NULL; /**< DSA signature */ char authenticate[] = "AUTHENTICATE PASSDSS-3DES-1\r\n"; char *BufferIn = NULL; char *BufferOut = NULL; char *BufferPtr = NULL; Buffer BufH; /**< Buffer to hold data used for signature. */ Buffer BufSpace; /**< Space to hold data before/after base64 conversion */ Buffer *Buf_H = &BufH; Buffer *Buf_Space = &BufSpace; BIO *bio_rw = NULL; /**< Memory buffer bio */ BIO *b64= NULL; /**< Base64 bio */ BIO *cipher = NULL; /**< Symmetric crypto bio */ BIGNUM *server_pubkey = NULL; /**< received server public DH key */ BIGNUM *dh_K = NULL; /**< DH computed shared secret */ u_char hash[20]; /**< SHA1 hash */ u_char enc_key[24]; /**< encryption key (3des) */ u_char enc_iv[8]; /**< initialization vector (3des) */ u_char int_key[20]; /**< cs integrity key */ u_char tmphash[40]; /**< temporary hash storage */ u_char hmac[EVP_MAX_MD_SIZE]; /**< HMAC for integrity of sent data (step L) */ int pklength = 0; /**< length of SSH-style DSA server public key */ int ssecmask = 0; /**< SASL security layers offered */ int sbuflen = 0; /**< maximum server security layer block size */ char *s = NULL; u_char num[3]; /**< Array to manupulate 3 octet number (sbuflen) */ /* Temporary variables */ int buflen, readlen, keysize, siglength; gogoc_status status = STATUS_SUCCESS_INIT; sint32_t tsp_status; /* From draft-newman-sasl-passdss-01. "This group was taken from the * ISAKMP/Oakley specification, and was originally generated by * Richard Schroeppel at the University of Arizona. Properties of * this prime are described in [Orm96]" */ /* RFC2409, DH group 2 (second Oakley group) */ static char *dh_group2= "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" "FFFFFFFF" "FFFFFFFF"; static unsigned char dh_g[]={ 0x02, }; /* Initialize Diffie Hellman variables */ if ((dh = DH_new()) == NULL || (server_pubkey = BN_new()) == NULL) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_GEN_MALLOC_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_MEMORY_STARVATION); goto error; } /* Convert dh_group2 and dh_g to BIGNUM type */ BN_hex2bn(&dh->p, dh_group2); dh->g = BN_bin2bn(dh_g,sizeof(dh_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, GOGO_STR_INITIALIZATION_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_AUTHENTICATION_FAILURE); goto error; } if ((dh_K = BN_new()) == NULL) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_GEN_MALLOC_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_MEMORY_STARVATION); goto error; } /* Reserve storage for DSA key */ if ((dsa = DSA_new()) == NULL || (dsa->p = BN_new()) == NULL || (dsa->q = BN_new()) == NULL || (dsa->g = BN_new()) == NULL || (dsa->pub_key = BN_new()) == NULL || (dsa->priv_key = BN_new()) == NULL) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_GEN_MALLOC_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_MEMORY_STARVATION); goto error; } /* Allocate memory for DSA signature */ if ((sig = DSA_SIG_new()) == NULL) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_GEN_MALLOC_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_MEMORY_STARVATION); goto error; } /* Initialize data buffers */ BufferIn = calloc(1, TSP_AUTH_PASSDSS_BUFFERSIZE); BufferOut = calloc(1, TSP_AUTH_PASSDSS_BUFFERSIZE); if ((BufferIn == NULL) || (BufferOut == NULL)) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_GEN_MALLOC_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_MEMORY_STARVATION); goto error; } buffer_init(Buf_Space); buffer_init(Buf_H); if (Buf_Space->buf == NULL || Buf_H->buf == NULL) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_GEN_MALLOC_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_MEMORY_STARVATION); goto error; } /* Create a read/write memory BIO. Memory is segment is * created and resized as needed. When BIO is destroyed, the * memory is freed. */ bio_rw = BIO_new(BIO_s_mem()); /* Create a base64 BIO filter */ b64 = BIO_new(BIO_f_base64()); if ((bio_rw == NULL) || (b64 == NULL)) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_GEN_MALLOC_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_MEMORY_STARVATION); goto error; } /* Compute the Diffie-Hellman public value "X" as follows. If X has a value of 0, repeat. x X = g mod n where g = dh_g = 2 n = dh_group2 x = DH secret key X = DH public key */ if (DH_generate_key(dh) == 0) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, GOGO_STR_DH_GEN_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_AUTHENTICATION_FAILURE); goto error; } /* Validate DH public key (RFC2631, 2.1.5) */ /* Send message with SASL mechanism identifier */ if ( nt->netsend(socket, authenticate, sizeof(authenticate)) == -1 ) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_NET_FAIL_W_SOCKET); status = make_status(CTX_TSPAUTHENTICATION, ERR_SOCKET_IO); goto error; } /* First PASSDSS message from client to server: string azname ; the user name to login as, may be empty if same as authentication name string authname ; the authentication name mpint X ; Diffie-Hellman parameter X */ /* azname is empty. Just insert a string length zero */ buffer_put_int(Buf_Space, 0); /* authname */ buffer_put_cstring(Buf_Space, conf->userid); /* DH public key */ buffer_put_bignum(Buf_Space, dh->pub_key); /* At this point, save the buffer into Buf_H. Used later for * signature verification. */ buffer_append(Buf_H, buffer_ptr(Buf_Space), buffer_len(Buf_Space)); /* Push base64 filter */ BIO_push(b64, bio_rw); /* no newline */ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); /* Write Buffer content into bio_rw. Buffer will be base64 * encoded. */ BIO_write(b64, buffer_ptr(Buf_Space), (int) buffer_len(Buf_Space)); BIO_flush(b64); /* Get pointer to the result */ buflen = BIO_get_mem_data(bio_rw, &BufferPtr); // Send data to server, save response in BufferIn. if((readlen = nt->netsendrecv(socket, BufferPtr, buflen, BufferIn, TSP_AUTH_PASSDSS_BUFFERSIZE)) == -1) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_NET_FAIL_RW_SOCKET); status = make_status(CTX_TSPAUTHENTICATION, ERR_SOCKET_IO); goto error; } /* remove base64 filter */ BIO_pop(bio_rw); buffer_clear(Buf_Space); buflen = 0; /* Decode response (base64) and extract server response * * The response format is as follows: uint32 pklength ; length of SSH-style DSA server public key (number of bytes up to y, inclusively) string "ssh-dss" ; constant string "ssh-dss" (lower case) mpint p ; DSA public key parameters mpint q mpint g mpint z (y in draft) mpint Y ; Diffie-Hellman parameter Y OCTET ssecmask ; SASL security layers offered 3 OCTET sbuflen ; maximum server security layer block size uint32 siglength ; length of SSH-style dss signature (number of bytes up to s inclusively) string "ssh-dss" ; constant string "ssh-dss" (lower case) mpint r ; DSA signature parameters mpint s */ buflen = base64decode(BufferOut, BufferIn); buffer_append(Buf_Space, BufferOut, buflen); /* Get pklength */ pklength = buffer_get_int(Buf_Space); /* Assuming that * p, g, and y are 512 bits, * q is 160 bits, * "ssh-dss" is 7 bytes * pklength should be at least 240 bytes. */ if (pklength < 240) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, GOGO_STR_RCVD_DATA_INVALID); status = make_status(CTX_TSPAUTHENTICATION, ERR_AUTHENTICATION_FAILURE); goto error; } /* Make a copy of (pklength|"ssh-dss"|p|q|g|z) in Buf_H */ /* Add pklength */ buffer_put_int(Buf_H, pklength); /* Add "ssh-dss"|p|q|g|z */ buffer_append(Buf_H, buffer_ptr(Buf_Space), pklength); /* Get "ssh-dss" string */ s = buffer_get_string(Buf_Space, (unsigned int*)&buflen); pal_free(s); s = NULL; /* Get p */ buffer_get_bignum(Buf_Space, dsa->p); /* Get q */ buffer_get_bignum(Buf_Space, dsa->q); /* Get g */ buffer_get_bignum(Buf_Space, dsa->g); /* Get z (pub_key) */ buffer_get_bignum(Buf_Space, dsa->pub_key); /* Get DH public key */ buffer_get_bignum(Buf_Space, server_pubkey); /* Copy in Buf_H for signature verification later */ buffer_put_bignum(Buf_H, server_pubkey); /* Buffer now points at ssecmask (1 octet), followed by * sbuflen (3 octets). Make a copy of these 4 octets in Buf_H * now, then extract these values. */ buffer_append(Buf_H, buffer_ptr(Buf_Space), 4); /* Get ssecmask */ ssecmask = buffer_get_octet(Buf_Space); /* Get sbuflen * Big endian binary unsigned integer */ buffer_get(Buf_Space, (char *)num, 3); sbuflen = (((u_long)(u_char)(num)[0] << 16) | ((u_long)(u_char)(num)[1] << 8) | ((u_long)(u_char)(num)[2])); /* DSS signature */ /* Get siglength */ siglength = buffer_get_int(Buf_Space); /* r and s are 20 bytes each, encoded as mpint (2*24) * "ssh-dss" is 7 bytes + int32 siglength should be >= 59 * octets (mpint may have leading zero byte) */ if (siglength < 59) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, GOGO_STR_RCVD_DATA_INVALID); status = make_status(CTX_TSPAUTHENTICATION, ERR_AUTHENTICATION_FAILURE); goto error; } /* Get "ssh-dss" string */ s = buffer_get_string(Buf_Space, (unsigned int*)&buflen); pal_free(s); s = NULL; /* Get DSA signature r and s*/ if ((sig->r= BN_new()) == NULL || (sig->s = BN_new()) == NULL) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_GEN_MALLOC_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_MEMORY_STARVATION); goto error; } /* Get r */ buffer_get_bignum(Buf_Space, sig->r); /* Get s */ buffer_get_bignum(Buf_Space, sig->s); /* Validate server DH public key (RFC2631, 2.1.5) */ { if( !add_dsakey_to_keyfile(dsa, conf->server, TSPC_DSA_KEYFILE, conf->no_questions) ) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, GOGO_STR_KEY_VERIF_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_AUTHENTICATION_FAILURE); goto error; } } /* Verify that DSA public key belongs to server */ /* Compute DH shared secret */ if ((s = calloc(1, DH_size(dh))) == NULL) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_GEN_MALLOC_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_MEMORY_STARVATION); goto error; } if( (keysize = DH_compute_key((unsigned char*)s, server_pubkey, dh)) < 0 ) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, GOGO_STR_DH_SHARED_COMPUTE_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_AUTHENTICATION_FAILURE); goto error; } BN_bin2bn((const unsigned char*)s, keysize, dh_K); memset(s, 0, keysize); pal_free(s); s = NULL; Display(LOG_LEVEL_3, ELDebug, TSP_AUTH_PASSDSS_STRING, GOGO_STR_DH_SHARED_KEY, BN_bn2hex(dh_K)); /* Append dh_K in to complete the buffer. Use Buffer to hold * result to keep Bf_H intact, since to will be used (without * dh_K) to compute HMAC for packet integrity. */ buffer_clear(Buf_Space); buffer_append(Buf_Space, buffer_ptr(Buf_H), buffer_len(Buf_H)); buffer_put_bignum(Buf_Space, dh_K); /* Compute SHA1 hash of Buffer */ SHA1(buffer_ptr(Buf_Space), buffer_len(Buf_Space), hash); /* Debug information available at level 4 */ { BIGNUM *h; h = BN_bin2bn(hash, 20, NULL); Display(LOG_LEVEL_3, ELDebug, TSP_AUTH_PASSDSS_STRING, GOGO_STR_SIGNED_HASH, BN_bn2hex(h)); BN_free(h); } Display(LOG_LEVEL_3, ELDebug, TSP_AUTH_PASSDSS_STRING, GOGO_STR_DSA_SIGN_R, BN_bn2hex(sig->r)); Display(LOG_LEVEL_3, ELDebug, TSP_AUTH_PASSDSS_STRING, GOGO_STR_DSA_SIGN_S, BN_bn2hex(sig->s)); // Verify that the DSS signature is a signature of hash. switch( DSA_do_verify(hash, sizeof(hash), sig, dsa) ) { case 0: Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, GOGO_STR_BAD_SIG_FROM_SERVER); status = make_status(CTX_TSPAUTHENTICATION, ERR_AUTHENTICATION_FAILURE); goto error; break; /* NOTREACHED */ case 1: /* correct signature */ break; default: /* -1 on error */ Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, GOGO_STR_SIG_VERIF_ERROR); status = make_status(CTX_TSPAUTHENTICATION, ERR_AUTHENTICATION_FAILURE); goto error; break; /* NOTREACHED */ } /* Step I: Compute 3DES key and iv */ /* cs-encryption-iv = SHA1( K || "A" || H ) sc-encryption-iv = SHA1( K || "B" || H ) cs-encryption-key-1 = SHA1( K || "C" || H ) cs-encryption-key-2 = SHA1( K || cs-encryption-key-1 ) cs-encryption-key = cs-encryption-key-1 || cs-encryption-key-2 sc-encryption-key-1 = SHA1( K || "D" || H ) sc-encryption-key-2 = SHA1( K || sc-encryption-key-1 ) sc-encryption-key = sc-encryption-key-1 || sc-encryption-key-2 cs-integrity-key = SHA1( K || "E" || H ) sc-integrity-key = SHA1( K || "F" || H ) K is dh_k in mpint format (string) H is hash */ /* Since we won't support SASL security layers, we need to * compute the following only: * cs-encryption-iv * cs-encryption-key * cs-integrity-key */ buffer_clear(Buf_Space); buffer_put_bignum(Buf_Space, dh_K); buffer_put_octet(Buf_Space,'A'); buffer_append(Buf_Space, hash, 20); SHA1(buffer_ptr(Buf_Space), buffer_len(Buf_Space), tmphash); /* Use first 8 octets as iv */ memcpy(enc_iv, tmphash, 8); buffer_clear(Buf_Space); buffer_put_bignum(Buf_Space, dh_K); buffer_put_octet(Buf_Space,'E'); buffer_append(Buf_Space, hash, 20); SHA1(buffer_ptr(Buf_Space), buffer_len(Buf_Space), int_key); buffer_clear(Buf_Space); buffer_put_bignum(Buf_Space, dh_K); buffer_put_octet(Buf_Space,'C'); buffer_append(Buf_Space, hash, 20); SHA1(buffer_ptr(Buf_Space), buffer_len(Buf_Space), tmphash); buffer_clear(Buf_Space); buffer_put_bignum(Buf_Space, dh_K); buffer_append(Buf_Space, tmphash, 20); SHA1(buffer_ptr(Buf_Space), buffer_len(Buf_Space), tmphash+20); /* Use first 24 octets as key */ memcpy(enc_key, tmphash, 24); { BIGNUM *enc, *i, *iv; enc = BN_bin2bn(enc_key, 24, NULL); iv = BN_bin2bn(enc_iv, 8, NULL); i = BN_bin2bn(int_key, 20, NULL); Display(LOG_LEVEL_3, ELDebug, TSP_AUTH_PASSDSS_STRING, GOGO_STR_PASSDS_ENC_KEY, BN_bn2hex(enc)); Display(LOG_LEVEL_3, ELDebug, TSP_AUTH_PASSDSS_STRING, GOGO_STR_PASSDS_IV, BN_bn2hex(iv)); Display(LOG_LEVEL_3, ELDebug, TSP_AUTH_PASSDSS_STRING, GOGO_STR_PASSDS_INTEG_KEY, BN_bn2hex(i)); BN_free(enc); BN_free(i); BN_free(iv); } /* (J) Create a buffer beginning with a bit mask for the selected security layer (it MUST be one offered from server) followed by three octets representing the maximum cipher-text buffer size (at least 32) the client can accept in network byte order. This is followed by a string containing the passphrase. */ buffer_clear(Buf_Space); buffer_put_octet(Buf_Space, ssecmask); buffer_put_octet(Buf_Space, 0); buffer_put_octet(Buf_Space, 0); buffer_put_octet(Buf_Space, 0); /**< @bug must be at least 32 */ buffer_put_cstring(Buf_Space, conf->passwd); /* (K) Create a buffer containing items (1) through (7) immediately followed by the first four octets of (J). */ buffer_append(Buf_H, buffer_ptr(Buf_Space), 4); /* (L) Compute HMAC-SHA-1 with (K) as the data and the cs-integrity- key from step (I) as the key. This produces a 20 octet result. */ HMAC(EVP_sha1(), int_key, sizeof(int_key), buffer_ptr(Buf_H), buffer_len(Buf_H), hmac, (unsigned int*)&keysize); /* (M) Create a buffer containing (J) followed by (L) followed by an arbitrary number of zero octets as necessary to reach the block size of DES and conceal the passphrase length from an eavesdropper. */ buffer_append(Buf_Space, hmac, keysize); /* (N) Apply the triple-DES algorithm to (M) with the first 8 octets of cs-encryption-iv from step (I) as the initialization vector and the first 24 octets of cs-encryption-key as the key. */ /* Padding is automatically done. From OpenSSL EVP_EncryptInit(3): EVP_CIPHER_CTX_set_padding() enables or disables padding. By default encryption operations are padded using standard block padding and the padding is checked and removed when decrypting. */ /* Create BIO filter to encrypt using 3des + convert to base64. Result is written in memory BIO. */ /* Erase BIO and buffer memory */ BIO_reset(bio_rw); memset(BufferOut, 0, TSP_AUTH_PASSDSS_BUFFERSIZE); memset(BufferIn, 0, TSP_AUTH_PASSDSS_BUFFERSIZE); buflen = 0; /* Create cipher BIO */ cipher = BIO_new(BIO_f_cipher()); BIO_set_cipher(cipher, EVP_des_ede3_cbc(), enc_key, enc_iv, 1); /* Assemble filters as cipher->b64->bio_rw */ BIO_push(cipher, b64); BIO_push(b64, bio_rw); /* Write Buffer content into bio_rw */ BIO_write(cipher, buffer_ptr(Buf_Space), (int) buffer_len(Buf_Space)); BIO_flush(cipher); /* Get pointer to the result. */ buflen = BIO_get_mem_data(bio_rw, &BufferPtr); /* wipe encryption material */ memset(enc_key, 0, sizeof(enc_key)); memset(enc_iv, 0, sizeof(enc_iv)); /* Send data to server, save response in BufferIn */ if( (readlen = nt->netsendrecv(socket, BufferPtr, buflen, BufferIn, TSP_AUTH_PASSDSS_BUFFERSIZE)) == -1) { Display(LOG_LEVEL_1, ELError, TSP_AUTH_PASSDSS_STRING, STR_NET_FAIL_RW_SOCKET); status = make_status(CTX_TSPAUTHENTICATION, ERR_SOCKET_IO); goto error; } tsp_status = tspGetStatusCode(BufferIn); // Check if the reply status indicated a broker redirection. if( tspIsRedirectStatus(tsp_status) ) { if( tspHandleRedirect(BufferIn, conf, broker_list) == TSP_REDIRECT_OK ) { status = make_status(CTX_TSPAUTHENTICATION, EVNT_BROKER_REDIRECTION); } else { // Redirect error. status = make_status(CTX_TSPAUTHENTICATION, ERR_BROKER_REDIRECTION); } goto error; } // Check if authentication was successful. switch( tsp_status ) { case TSP_PROTOCOL_SUCCESS: break; case TSP_PROTOCOL_AUTH_FAILED: Display(LOG_LEVEL_1, ELError, "AuthPASSDSS_3DES_1", STR_TSP_AUTH_FAILED_USER, conf->userid); status = make_status(CTX_TSPAUTHENTICATION, ERR_AUTHENTICATION_FAILURE); goto error; default: Display(LOG_LEVEL_1, ELError, "AuthPASSDSS_3DES_1", STR_TSP_UNKNOWN_ERR_AUTH_FAILED, tspGetTspStatusStr(tsp_status)); status = make_status(CTX_TSPAUTHENTICATION, ERR_TSP_GENERIC_ERROR); goto error; } status = STATUS_SUCCESS_INIT; error: /* Free storage for DSA key */ if (dsa != NULL) DSA_free(dsa); /* Also frees BIGNUMs inside struct */ /* DSA signature */ if (sig != NULL) DSA_SIG_free(sig); /* Free Diffie Hellman variables */ if (dh != NULL) DH_free(dh); /* Also frees BIGNUMs inside struct */ if (server_pubkey != NULL) BN_free(server_pubkey); if (dh_K != NULL) BN_free(dh_K); /* Buffers */ if (Buf_Space->buf != NULL) buffer_free(Buf_Space); if (Buf_H->buf != NULL) buffer_free(Buf_H); /* malloc'ed space*/ if (BufferIn != NULL) pal_free(BufferIn); if (BufferOut != NULL) pal_free(BufferOut); /* BIOs */ if (cipher != NULL) BIO_vfree(cipher); if (b64 != NULL) BIO_vfree(b64); if (bio_rw != NULL) BIO_vfree(bio_rw); /* strings buffers */ if (s != NULL) pal_free(s); return status; }
static void process(void) { u_int msg_len, buf_len, consumed, type, i; u_char *cp; u_int32_t id; buf_len = buffer_len(&iqueue); if (buf_len < 5) return; /* Incomplete message. */ cp = buffer_ptr(&iqueue); msg_len = get_u32(cp); if (msg_len > SFTP_MAX_MSG_LENGTH) { error("bad message from %s local user %s", client_addr, pw->pw_name); sftp_server_cleanup_exit(11); } if (buf_len < msg_len + 4) return; buffer_consume(&iqueue, 4); buf_len -= 4; type = buffer_get_char(&iqueue); switch (type) { case SSH2_FXP_INIT: process_init(); init_done = 1; break; case SSH2_FXP_EXTENDED: if (!init_done) fatal("Received extended request before init"); id = get_int(); process_extended(id); break; default: if (!init_done) fatal("Received %u request before init", type); id = get_int(); for (i = 0; handlers[i].handler != NULL; i++) { if (type == handlers[i].type) { if (!request_permitted(&handlers[i])) { send_status(id, SSH2_FX_PERMISSION_DENIED); } else { handlers[i].handler(id); } break; } } if (handlers[i].handler == NULL) #ifdef NERSC_MOD { s_audit("sftp_process_unknown_3", "count=%i int=%d uristring=%d", get_client_session_id(), (int)getppid(), type); #endif error("Unknown message %u", type); #ifdef NERSC_MOD } #endif } /* discard the remaining bytes from the current packet */ if (buf_len < buffer_len(&iqueue)) { error("iqueue grew unexpectedly"); sftp_server_cleanup_exit(255); } consumed = buf_len - buffer_len(&iqueue); if (msg_len < consumed) { error("msg_len %u < consumed %u", msg_len, consumed); sftp_server_cleanup_exit(255); } if (msg_len > consumed) buffer_consume(&iqueue, msg_len - consumed); }
static void process(void) { u_int msg_len; u_int buf_len; u_int consumed; u_int type; u_char *cp; buf_len = buffer_len(&iqueue); if (buf_len < 5) return; /* Incomplete message. */ cp = buffer_ptr(&iqueue); msg_len = get_u32(cp); if (msg_len > SFTP_MAX_MSG_LENGTH) { error("bad message from %s local user %s", client_addr, pw->pw_name); sftp_server_cleanup_exit(11); } if (buf_len < msg_len + 4) return; buffer_consume(&iqueue, 4); buf_len -= 4; type = buffer_get_char(&iqueue); switch (type) { case SSH2_FXP_INIT: process_init(); break; case SSH2_FXP_OPEN: process_open(); break; case SSH2_FXP_CLOSE: process_close(); break; case SSH2_FXP_READ: process_read(); break; case SSH2_FXP_WRITE: process_write(); break; case SSH2_FXP_LSTAT: process_lstat(); break; case SSH2_FXP_FSTAT: process_fstat(); break; case SSH2_FXP_SETSTAT: process_setstat(); break; case SSH2_FXP_FSETSTAT: process_fsetstat(); break; case SSH2_FXP_OPENDIR: process_opendir(); break; case SSH2_FXP_READDIR: process_readdir(); break; case SSH2_FXP_REMOVE: process_remove(); break; case SSH2_FXP_MKDIR: process_mkdir(); break; case SSH2_FXP_RMDIR: process_rmdir(); break; case SSH2_FXP_REALPATH: process_realpath(); break; case SSH2_FXP_STAT: process_stat(); break; case SSH2_FXP_RENAME: process_rename(); break; case SSH2_FXP_READLINK: process_readlink(); break; case SSH2_FXP_SYMLINK: process_symlink(); break; case SSH2_FXP_EXTENDED: process_extended(); break; default: error("Unknown message %d", type); break; } /* discard the remaining bytes from the current packet */ if (buf_len < buffer_len(&iqueue)) { error("iqueue grew unexpectedly"); sftp_server_cleanup_exit(255); } consumed = buf_len - buffer_len(&iqueue); if (msg_len < consumed) { error("msg_len %d < consumed %d", msg_len, consumed); sftp_server_cleanup_exit(255); } if (msg_len > consumed) buffer_consume(&iqueue, msg_len - consumed); }
int sftp_server_main(int argc, char **argv, struct passwd *user_pw) { fd_set *rset, *wset; int i, in, out, max, ch, skipargs = 0, log_stderr = 0; ssize_t len, olen, set_size; SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; char *cp, *homedir = NULL, buf[4*4096]; long mask; extern char *optarg; extern char *__progname; __progname = ssh_get_progname(argv[0]); log_init(__progname, log_level, log_facility, log_stderr); pw = pwcopy(user_pw); while (!skipargs && (ch = getopt(argc, argv, "d:f:l:P:p:Q:u:cehR")) != -1) { switch (ch) { case 'Q': if (strcasecmp(optarg, "requests") != 0) { fprintf(stderr, "Invalid query type\n"); exit(1); } for (i = 0; handlers[i].handler != NULL; i++) printf("%s\n", handlers[i].name); for (i = 0; extended_handlers[i].handler != NULL; i++) printf("%s\n", extended_handlers[i].name); exit(0); break; case 'R': readonly = 1; break; case 'c': /* * Ignore all arguments if we are invoked as a * shell using "sftp-server -c command" */ skipargs = 1; break; case 'e': log_stderr = 1; break; case 'l': log_level = log_level_number(optarg); if (log_level == SYSLOG_LEVEL_NOT_SET) error("Invalid log level \"%s\"", optarg); break; case 'f': log_facility = log_facility_number(optarg); if (log_facility == SYSLOG_FACILITY_NOT_SET) error("Invalid log facility \"%s\"", optarg); break; case 'd': cp = tilde_expand_filename(optarg, user_pw->pw_uid); homedir = percent_expand(cp, "d", user_pw->pw_dir, "u", user_pw->pw_name, (char *)NULL); free(cp); break; case 'p': if (request_whitelist != NULL) fatal("Permitted requests already set"); request_whitelist = xstrdup(optarg); break; case 'P': if (request_blacklist != NULL) fatal("Refused requests already set"); request_blacklist = xstrdup(optarg); break; case 'u': errno = 0; mask = strtol(optarg, &cp, 8); if (mask < 0 || mask > 0777 || *cp != '\0' || cp == optarg || (mask == 0 && errno != 0)) fatal("Invalid umask \"%s\"", optarg); (void)umask((mode_t)mask); break; case 'h': default: sftp_server_usage(); } } log_init(__progname, log_level, log_facility, log_stderr); if ((cp = getenv("SSH_CONNECTION")) != NULL) { client_addr = xstrdup(cp); if ((cp = strchr(client_addr, ' ')) == NULL) { error("Malformed SSH_CONNECTION variable: \"%s\"", getenv("SSH_CONNECTION")); sftp_server_cleanup_exit(255); } *cp = '\0'; } else client_addr = xstrdup("UNKNOWN"); logit("session opened for local user %s from [%s]", pw->pw_name, client_addr); in = STDIN_FILENO; out = STDOUT_FILENO; #ifdef HAVE_CYGWIN setmode(in, O_BINARY); setmode(out, O_BINARY); #endif max = 0; if (in > max) max = in; if (out > max) max = out; buffer_init(&iqueue); buffer_init(&oqueue); set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); rset = (fd_set *)xmalloc(set_size); wset = (fd_set *)xmalloc(set_size); if (homedir != NULL) { if (chdir(homedir) != 0) { error("chdir to \"%s\" failed: %s", homedir, strerror(errno)); } } #ifdef NERSC_MOD char* t1buf = encode_string(pw->pw_name, strlen(pw->pw_name)); s_audit("sftp_process_init_3", "count=%i int=%d uristring=%s addr=%s", get_client_session_id(), (int)getppid(), t1buf, client_addr); free(t1buf); #endif for (;;) { memset(rset, 0, set_size); memset(wset, 0, set_size); /* * Ensure that we can read a full buffer and handle * the worst-case length packet it can generate, * otherwise apply backpressure by stopping reads. */ if (buffer_check_alloc(&iqueue, sizeof(buf)) && buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) FD_SET(in, rset); olen = buffer_len(&oqueue); if (olen > 0) FD_SET(out, wset); if (select(max+1, rset, wset, NULL, NULL) < 0) { if (errno == EINTR) continue; error("select: %s", strerror(errno)); sftp_server_cleanup_exit(2); } /* copy stdin to iqueue */ if (FD_ISSET(in, rset)) { len = read(in, buf, sizeof buf); if (len == 0) { debug("read eof"); sftp_server_cleanup_exit(0); } else if (len < 0) { error("read: %s", strerror(errno)); sftp_server_cleanup_exit(1); } else { buffer_append(&iqueue, buf, len); } } /* send oqueue to stdout */ if (FD_ISSET(out, wset)) { len = write(out, buffer_ptr(&oqueue), olen); if (len < 0) { error("write: %s", strerror(errno)); sftp_server_cleanup_exit(1); } else { buffer_consume(&oqueue, len); } } /* * Process requests from client if we can fit the results * into the output buffer, otherwise stop processing input * and let the output queue drain. */ if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) process(); } }
static int userauth_pubkey(Authctxt *authctxt) { #ifdef WIN32_FIXME int loginStat = 1; char currentUser[MAX_PATH] = {0}; DWORD currentUserSize = sizeof(currentUser); int targetIsCurrent = 0; # ifdef USE_NTCREATETOKEN int doOpenSSHVerify = 1; # else int doOpenSSHVerify = 0; # endif #endif Buffer b; Key *key = NULL; char *pkalg, *userstyle; u_char *pkblob, *sig; u_int alen, blen, slen; int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { debug2("userauth_pubkey: disabled because of invalid user"); return 0; } have_sig = packet_get_char(); if (datafellows & SSH_BUG_PKAUTH) { debug2("userauth_pubkey: SSH_BUG_PKAUTH"); /* no explicit pkalg given */ pkblob = packet_get_string(&blen); buffer_init(&b); buffer_append(&b, pkblob, blen); /* so we have to extract the pkalg from the pkblob */ pkalg = buffer_get_string(&b, &alen); buffer_free(&b); } else { pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); } pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("userauth_pubkey: unsupported public key algorithm: %s", pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("userauth_pubkey: cannot decode key: %s", pkalg); goto done; } if (key->type != pktype) { error("userauth_pubkey: type mismatch for decoded key " "(received %d, expected %d)", key->type, pktype); goto done; } if (key_type_plain(key->type) == KEY_RSA && (datafellows & SSH_BUG_RSASIGMD5) != 0) { logit("Refusing RSA key because client uses unsafe " "signature scheme"); goto done; } if (auth2_userkey_already_used(authctxt, key)) { logit("refusing previously-used %s key", key_type(key)); goto done; } if (match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, 0) != 1) { logit("%s: key type %s not in PubkeyAcceptedKeyTypes", __func__, sshkey_ssh_name(key)); goto done; } if (have_sig) { sig = packet_get_string(&slen); packet_check_eom(); buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); } else { buffer_put_string(&b, session_id2, session_id2_len); } /* reconstruct packet */ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", authctxt->style ? authctxt->style : ""); buffer_put_cstring(&b, userstyle); free(userstyle); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, "publickey"); buffer_put_char(&b, have_sig); buffer_put_cstring(&b, pkalg); } buffer_put_string(&b, pkblob, blen); #ifdef DEBUG_PK buffer_dump(&b); #endif pubkey_auth_info(authctxt, key, NULL); /* test for correct signature */ authenticated = 0; /* * On pure win32 try to logon using lsa first. */ #ifdef WIN32_FIXME authctxt -> hTokenLsa_ = NULL; authctxt -> methoddata = NULL; /* * Retrieve name of current login user (i.e. sshd process owner). */ GetUserName(currentUser, ¤tUserSize); /* * Try to get token from lsa, but only if targetUser != currentUser. * Owerthise we already have targetUser's token in current thread, so * we only need key verify from original OpenSSH code. */ targetIsCurrent = (strcmp(currentUser, authctxt -> user) == 0); if (targetIsCurrent) { doOpenSSHVerify = 1; } else { loginStat = LsaLogon(&authctxt -> hTokenLsa_, HomeDirLsaW, authctxt -> user, pkblob, blen, sig, slen, buffer_ptr(&b), buffer_len(&b), datafellows); /* * If lsa logon process success. */ if (loginStat == 0) { /* * And user authorized OK. */ if (authctxt -> hTokenLsa_) { doOpenSSHVerify = 0; /* * This is part of openssh authorization needed for parsing * 'options' block in key. */ authctxt -> pw -> pw_dir = GetHomeDir(authctxt -> user); if (PRIVSEP(user_key_allowed(authctxt -> pw, key, 1))) // PRAGMA:TODO { authenticated = 1; } else { authenticated = 0; } buffer_free(&b); free(sig); } } } if (doOpenSSHVerify) { /* * If lsa fails, test for correct signature using openssh code. */ authctxt -> pw -> pw_dir = GetHomeDir(authctxt -> user); if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0)) //PRAGMA:TODO && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) { authenticated = 1; } } /* * Original code. */ #else /* #ifdef WIN32_FIXME */ if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) { authenticated = 1; /* Record the successful key to prevent reuse */ auth2_record_userkey(authctxt, key); key = NULL; /* Don't free below */ } buffer_free(&b); free(sig); #endif /* else #ifdef WIN32_FIXME. */ } else { debug("test whether pkalg/pkblob are acceptable"); packet_check_eom(); /* XXX fake reply and always send PK_OK ? */ /* * XXX this allows testing whether a user is allowed * to login: if you happen to have a valid pubkey this * message is sent. the message is NEVER sent at all * if a user is not allowed to login. is this an * issue? -markus */ #ifndef WIN32_FIXME if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) #endif { packet_start(SSH2_MSG_USERAUTH_PK_OK); packet_put_string(pkalg, alen); packet_put_string(pkblob, blen); packet_send(); packet_write_wait(); authctxt->postponed = 1; } } if (authenticated != 1) auth_clear_options(); done: debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); if (key != NULL) key_free(key); free(pkalg); free(pkblob); return authenticated; }
void hcache_readfile(HCACHEFILE *file) { HCACHEDATA cachedata, *c, *last = 0; FILE *f; int bad_cache = 1, ch; const char *version; BUFFER buff; long buffsize; /* if( ! (hcachename = hcache_filename()) ) return;*/ if( ! (f = fopen( file->cachefilename, "rb" )) ) return; fseek( f, 0, SEEK_END ); buffsize = ftell( f ); fseek( f, 0, SEEK_SET ); buffer_init( &buff ); buffer_resize( &buff, buffsize + 1 ); if ( fread( buffer_ptr( &buff ), buffsize, 1, f ) != 1 ) { fclose( f ); goto bail; } buffer_ptr( &buff )[buffsize] = 0; fclose( f ); version = read_string( &buff ); ch = buffer_getchar( &buff ); if (!version || strcmp( version, CACHE_FILE_VERSION ) || ch != '\n' ) { goto bail; } for(;;) { int i, count, ch; LIST *l; c = &cachedata; c->boundname = read_string( &buff ); if( !c->boundname ) /* Test for eof */ break; c->time = read_int( &buff ); c->age = read_int( &buff ) + 1; /* we're getting older... */ #ifdef OPT_BUILTIN_MD5CACHE_EXT c->mtime = read_int( &buff ); read_md5sum( &buff, c->rulemd5sum ); memcpy( &c->currentrulemd5sum, &c->rulemd5sum, MD5_SUMSIZE ); read_md5sum( &buff, c->contentmd5sum ); memcpy( &c->currentcontentmd5sum, &c->contentmd5sum, MD5_SUMSIZE ); #endif if( !c->boundname ) goto bail; /* headers */ count = read_int( &buff ); for( l = 0, i = 0; i < count; ++i ) { const char *s = read_string( &buff ); if( !s ) goto bail; l = list_append( l, s, 0 ); } c->includes = l; /* hdrscan */ count = read_int( &buff ); for( l = 0, i = 0; i < count; ++i ) { const char *s = read_string( &buff ); if( !s ) goto bail; l = list_append( l, s, 0 ); } c->hdrscan = l; /* Read the newline */ ch = skip_spaces( &buff ); if( ch != '!' ) goto bail; ch = skip_spaces( &buff ); if( ch != '\n' ) goto bail; if( !hashenter( hcachehash, (HASHDATA **)&c ) ) { printf( "jam: can't insert header cache item, bailing on %s\n", file->cachefilename ); goto bail; } c->file = file; c->next = 0; if( last ) last->next = c; else file->hcachelist = c; last = c; } bad_cache = 0; if( DEBUG_HEADER ) printf( "hcache read from file %s\n", file->cachefilename ); bail: /* If its bad, no worries, it'll be overwritten in hcache_done() */ if( bad_cache ) printf( "jam: warning: the cache was invalid: %s\n", file->cachefilename ); buffer_free( &buff ); }
/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */ int ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp, const u_char *data, u_int datalen) { const EVP_MD *evp_md; EVP_MD_CTX md; u_char digest[EVP_MAX_MD_SIZE], *sig; u_int slen, dlen, len; int ok, nid; Buffer b; if (key == NULL || key->rsa == NULL || (key->type != KEY_RSA && key->type != KEY_RSA_CERT && key->type != KEY_RSA_CERT_V00)) { error("ssh_rsa_sign: no RSA key"); return -1; } nid = (datafellows & SSH_BUG_RSASIGMD5) ? NID_md5 : NID_sha1; if ((evp_md = EVP_get_digestbynid(nid)) == NULL) { error("ssh_rsa_sign: EVP_get_digestbynid %d failed", nid); return -1; } EVP_DigestInit(&md, evp_md); EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); slen = RSA_size(key->rsa); sig = xmalloc(slen); ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa); memset(digest, 'd', sizeof(digest)); if (ok != 1) { int ecode = ERR_get_error(); error("ssh_rsa_sign: RSA_sign failed: %s", ERR_error_string(ecode, NULL)); xfree(sig); return -1; } if (len < slen) { u_int diff = slen - len; debug("slen %u > len %u", slen, len); memmove(sig + diff, sig, len); memset(sig, 0, diff); } else if (len > slen) { error("ssh_rsa_sign: slen %u slen2 %u", slen, len); xfree(sig); return -1; } /* encode signature */ buffer_init(&b); buffer_put_cstring(&b, "ssh-rsa"); buffer_put_string(&b, sig, slen); len = buffer_len(&b); if (lenp != NULL) *lenp = len; if (sigp != NULL) { *sigp = xmalloc(len); memcpy(*sigp, buffer_ptr(&b), len); } buffer_free(&b); memset(sig, 's', slen); xfree(sig); return 0; }
static int userauth_pubkey(Authctxt *authctxt) { Buffer b; Key *key = NULL; char *pkalg, *userstyle; u_char *pkblob, *sig; u_int alen, blen, slen; int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { debug2("userauth_pubkey: disabled because of invalid user"); return 0; } have_sig = packet_get_char(); if (datafellows & SSH_BUG_PKAUTH) { debug2("userauth_pubkey: SSH_BUG_PKAUTH"); /* no explicit pkalg given */ pkblob = packet_get_string(&blen); buffer_init(&b); buffer_append(&b, pkblob, blen); /* so we have to extract the pkalg from the pkblob */ pkalg = buffer_get_string(&b, &alen); buffer_free(&b); } else { pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); } pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("userauth_pubkey: unsupported public key algorithm: %s", pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("userauth_pubkey: cannot decode key: %s", pkalg); goto done; } if (key->type != pktype) { error("userauth_pubkey: type mismatch for decoded key " "(received %d, expected %d)", key->type, pktype); goto done; } if (key_type_plain(key->type) == KEY_RSA && (datafellows & SSH_BUG_RSASIGMD5) != 0) { logit("Refusing RSA key because client uses unsafe " "signature scheme"); goto done; } if (auth2_userkey_already_used(authctxt, key)) { logit("refusing previously-used %s key", key_type(key)); goto done; } if (match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, strlen(options.pubkey_key_types), 0) != 1) { logit("%s: key type %s not in PubkeyAcceptedKeyTypes", __func__, sshkey_ssh_name(key)); goto done; } if (have_sig) { sig = packet_get_string(&slen); packet_check_eom(); buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); } else { buffer_put_string(&b, session_id2, session_id2_len); } /* reconstruct packet */ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", authctxt->style ? authctxt->style : ""); buffer_put_cstring(&b, userstyle); free(userstyle); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, "publickey"); buffer_put_char(&b, have_sig); buffer_put_cstring(&b, pkalg); } buffer_put_string(&b, pkblob, blen); #ifdef DEBUG_PK buffer_dump(&b); #endif pubkey_auth_info(authctxt, key, NULL); /* test for correct signature */ authenticated = 0; if (PRIVSEP(user_key_allowed(authctxt->pw, key)) && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) { authenticated = 1; /* Record the successful key to prevent reuse */ auth2_record_userkey(authctxt, key); key = NULL; /* Don't free below */ } buffer_free(&b); free(sig); } else { debug("test whether pkalg/pkblob are acceptable"); packet_check_eom(); /* XXX fake reply and always send PK_OK ? */ /* * XXX this allows testing whether a user is allowed * to login: if you happen to have a valid pubkey this * message is sent. the message is NEVER sent at all * if a user is not allowed to login. is this an * issue? -markus */ if (PRIVSEP(user_key_allowed(authctxt->pw, key))) { packet_start(SSH2_MSG_USERAUTH_PK_OK); packet_put_string(pkalg, alen); packet_put_string(pkblob, blen); packet_send(); packet_write_wait(); authctxt->postponed = 1; } } if (authenticated != 1) auth_clear_options(); done: debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); if (key != NULL) key_free(key); free(pkalg); free(pkblob); return authenticated; }
int ssh_dss_sign(const Key *key, u_char **sigp, u_int *lenp, const u_char *data, u_int datalen) { DSA_SIG *sig; const EVP_MD *evp_md = EVP_sha1(); EVP_MD_CTX md; u_char digest[EVP_MAX_MD_SIZE], sigblob[SIGBLOB_LEN]; u_int rlen, slen, len, dlen; Buffer b; if (key == NULL || key->dsa == NULL || (key->type != KEY_DSA && key->type != KEY_DSA_CERT && key->type != KEY_DSA_CERT_V00)) { error("ssh_dss_sign: no DSA key"); return -1; } EVP_DigestInit(&md, evp_md); EVP_DigestUpdate(&md, data, datalen); EVP_DigestFinal(&md, digest, &dlen); sig = DSA_do_sign(digest, dlen, key->dsa); memset(digest, 'd', sizeof(digest)); if (sig == NULL) { error("ssh_dss_sign: sign failed"); return -1; } rlen = BN_num_bytes(sig->r); slen = BN_num_bytes(sig->s); if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { error("bad sig size %u %u", rlen, slen); DSA_SIG_free(sig); return -1; } memset(sigblob, 0, SIGBLOB_LEN); BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen); BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen); DSA_SIG_free(sig); if (datafellows & SSH_BUG_SIGBLOB) { if (lenp != NULL) *lenp = SIGBLOB_LEN; if (sigp != NULL) { *sigp = xmalloc(SIGBLOB_LEN); memcpy(*sigp, sigblob, SIGBLOB_LEN); } } else { /* ietf-drafts */ buffer_init(&b); buffer_put_cstring(&b, "ssh-dss"); buffer_put_string(&b, sigblob, SIGBLOB_LEN); len = buffer_len(&b); if (lenp != NULL) *lenp = len; if (sigp != NULL) { *sigp = xmalloc(len); memcpy(*sigp, buffer_ptr(&b), len); } buffer_free(&b); } return 0; }
void kexecdh_client(Kex *kex) { EC_KEY *client_key; EC_POINT *server_public; const EC_GROUP *group; BIGNUM *shared_secret; Key *server_host_key; u_char *server_host_key_blob = NULL, *signature = NULL; u_char *kbuf, *hash; u_int klen, slen, sbloblen, hashlen; if ((client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) fatal("%s: EC_KEY_new_by_curve_name failed", __func__); if (EC_KEY_generate_key(client_key) != 1) fatal("%s: EC_KEY_generate_key failed", __func__); group = EC_KEY_get0_group(client_key); packet_start(SSH2_MSG_KEX_ECDH_INIT); packet_put_ecpoint(group, EC_KEY_get0_public_key(client_key)); packet_send(); debug("sending SSH2_MSG_KEX_ECDH_INIT"); #ifdef DEBUG_KEXECDH fputs("client private key:\n", stderr); key_dump_ec_key(client_key); #endif debug("expecting SSH2_MSG_KEX_ECDH_REPLY"); packet_read_expect(SSH2_MSG_KEX_ECDH_REPLY); /* hostkey */ server_host_key_blob = packet_get_string(&sbloblen); server_host_key = key_from_blob(server_host_key_blob, sbloblen); if (server_host_key == NULL) fatal("cannot decode server_host_key_blob"); if (server_host_key->type != kex->hostkey_type) fatal("type mismatch for decoded server_host_key_blob"); if (kex->verify_host_key == NULL) fatal("cannot verify server_host_key"); if (kex->verify_host_key(server_host_key) == -1) fatal("server_host_key verification failed"); /* Q_S, server public key */ if ((server_public = EC_POINT_new(group)) == NULL) fatal("%s: EC_POINT_new failed", __func__); packet_get_ecpoint(group, server_public); if (key_ec_validate_public(group, server_public) != 0) fatal("%s: invalid server public key", __func__); #ifdef DEBUG_KEXECDH fputs("server public key:\n", stderr); key_dump_ec_point(group, server_public); #endif /* signed H */ signature = packet_get_string(&slen); packet_check_eom(); klen = (EC_GROUP_get_degree(group) + 7) / 8; kbuf = xmalloc(klen); if (ECDH_compute_key(kbuf, klen, server_public, client_key, NULL) != (int)klen) fatal("%s: ECDH_compute_key failed", __func__); #ifdef DEBUG_KEXECDH dump_digest("shared secret", kbuf, klen); #endif if ((shared_secret = BN_new()) == NULL) fatal("%s: BN_new failed", __func__); if (BN_bin2bn(kbuf, klen, shared_secret) == NULL) fatal("%s: BN_bin2bn failed", __func__); memset(kbuf, 0, klen); free(kbuf); /* calc and verify H */ kex_ecdh_hash( kex->evp_md, group, kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->my), buffer_len(&kex->my), buffer_ptr(&kex->peer), buffer_len(&kex->peer), server_host_key_blob, sbloblen, EC_KEY_get0_public_key(client_key), server_public, shared_secret, &hash, &hashlen ); free(server_host_key_blob); EC_POINT_clear_free(server_public); EC_KEY_free(client_key); if (key_verify(server_host_key, signature, slen, hash, hashlen) != 1) fatal("key_verify failed for server_host_key"); key_free(server_host_key); free(signature); /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); }
if (bit_count > MAX_BITBUF_BIT_WRITE) { write_bits(&state->bitbuf, header_bits, MAX_BITBUF_BIT_WRITE); header_bits >>= MAX_BITBUF_BIT_WRITE; bit_count -= MAX_BITBUF_BIT_WRITE; } write_bits(&state->bitbuf, header_bits, bit_count); /* check_space flushes extra bytes in bitbuf. Required because * write_bits_always fails when the next commit makes the buffer * length exceed 64 bits */ check_space(&state->bitbuf, FORCE_FLUSH); count = buffer_used(&state->bitbuf); stream->next_out = buffer_ptr(&state->bitbuf); stream->avail_out -= count; stream->total_out += count; return COMP_OK; } void write_header(struct isal_zstream *stream) { struct isal_zstate *state = &stream->internal_state; struct isal_hufftables *hufftables = stream->hufftables; uint32_t count; state->state = ZSTATE_HDR; if (state->bitbuf.m_bit_count != 0) {
static int userauth_hostbased(Authctxt *authctxt) { Buffer b; Key *key = NULL; char *pkalg, *cuser, *chost, *service; u_char *pkblob, *sig; u_int alen, blen, slen; int pktype; int authenticated = 0; if (!authctxt->valid) { debug2("userauth_hostbased: disabled because of invalid user"); return 0; } pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); chost = packet_get_string(NULL); cuser = packet_get_string(NULL); sig = packet_get_string(&slen); debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d", cuser, chost, pkalg, slen); #ifdef DEBUG_PK debug("signature:"); buffer_init(&b); buffer_append(&b, sig, slen); buffer_dump(&b); buffer_free(&b); #endif pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("userauth_hostbased: unsupported " "public key algorithm: %s", pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("userauth_hostbased: cannot decode key: %s", pkalg); goto done; } if (key->type != pktype) { error("userauth_hostbased: type mismatch for decoded key " "(received %d, expected %d)", key->type, pktype); goto done; } service = datafellows & SSH_BUG_HBSERVICE ? __UNCONST("ssh-userauth") : authctxt->service; buffer_init(&b); buffer_put_string(&b, session_id2, session_id2_len); /* reconstruct packet */ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->user); buffer_put_cstring(&b, service); buffer_put_cstring(&b, "hostbased"); buffer_put_string(&b, pkalg, alen); buffer_put_string(&b, pkblob, blen); buffer_put_cstring(&b, chost); buffer_put_cstring(&b, cuser); #ifdef DEBUG_PK buffer_dump(&b); #endif /* test for allowed key and correct signature */ authenticated = 0; if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) authenticated = 1; buffer_free(&b); done: debug2("userauth_hostbased: authenticated %d", authenticated); if (key != NULL) key_free(key); xfree(pkalg); xfree(pkblob); xfree(cuser); xfree(chost); xfree(sig); return authenticated; }
static uint32_t write_constant_compressed_stateless(struct isal_zstream *stream, uint32_t repeated_char, uint32_t repeated_length, uint32_t end_of_stream) { /* Assumes repeated_length is at least 1. * Assumes the input end_of_stream is either 0 or 1. */ struct isal_zstate *state = &stream->internal_state; uint32_t rep_bits = ((repeated_length - 1) / 258) * 2; uint32_t rep_bytes = rep_bits / 8; uint32_t rep_extra = (repeated_length - 1) % 258; uint32_t bytes; /* Guarantee there is enough space for the header even in the worst case */ if (stream->avail_out < HEADER_LENGTH + MAX_FIXUP_CODE_LENGTH + rep_bytes + 8) return STATELESS_OVERFLOW; /* Assumes the repeated char is either 0 or 0xFF. */ memcpy(stream->next_out, repeated_char_header[repeated_char & 1], HEADER_LENGTH); if (end_of_stream > 0) stream->next_out[0] |= 1; memset(stream->next_out + HEADER_LENGTH, 0, rep_bytes); stream->avail_out -= HEADER_LENGTH + rep_bytes; stream->next_out += HEADER_LENGTH + rep_bytes; stream->total_out += HEADER_LENGTH + rep_bytes; set_buf(&state->bitbuf, stream->next_out, stream->avail_out); /* These two lines are basically a modified version of init. */ state->bitbuf.m_bits = 0; state->bitbuf.m_bit_count = rep_bits % 8; /* Add smaller repeat codes as necessary. Code280 can describe repeat * lengths of 115-130 bits. Code10 can describe repeat lengths of 10 * bits. If more than 230 bits, fill code with two code280s. Else if * more than 115 repeates, fill with code10s until one code280 can * finish the rest of the repeats. Else, fill with code10s and * literals */ if (rep_extra > 115) { while (rep_extra > 130 && rep_extra < 230) { write_bits(&state->bitbuf, CODE_10, CODE_10_LENGTH); rep_extra -= 10; } if (rep_extra >= 230) { write_bits(&state->bitbuf, CODE_280 | ((rep_extra / 2 - 115) << CODE_280_LENGTH), CODE_280_TOTAL_LENGTH); rep_extra -= rep_extra / 2; } write_bits(&state->bitbuf, CODE_280 | ((rep_extra - 115) << CODE_280_LENGTH), CODE_280_TOTAL_LENGTH); } else { while (rep_extra >= 10) { write_bits(&state->bitbuf, CODE_10, CODE_10_LENGTH); rep_extra -= 10; } for (; rep_extra > 0; rep_extra--) write_bits(&state->bitbuf, CODE_LIT, CODE_LIT_LENGTH); } write_bits(&state->bitbuf, END_OF_BLOCK, END_OF_BLOCK_LEN); stream->next_in += repeated_length; stream->avail_in -= repeated_length; stream->total_in += repeated_length; bytes = buffer_used(&state->bitbuf); stream->next_out = buffer_ptr(&state->bitbuf); stream->avail_out -= bytes; stream->total_out += bytes; return COMP_OK; }
static int ssh_session(void) { int type; int interactive = 0; int have_tty = 0; struct winsize ws; char *cp; const char *display; /* Enable compression if requested. */ if (options.compression) { debug("Requesting compression at level %d.", options.compression_level); if (options.compression_level < 1 || options.compression_level > 9) fatal("Compression level must be from 1 (fast) to " "9 (slow, best)."); /* Send the request. */ packet_start(SSH_CMSG_REQUEST_COMPRESSION); packet_put_int(options.compression_level); packet_send(); packet_write_wait(); type = packet_read(); if (type == SSH_SMSG_SUCCESS) packet_start_compression(options.compression_level); else if (type == SSH_SMSG_FAILURE) logit("Warning: Remote host refused compression."); else packet_disconnect("Protocol error waiting for " "compression response."); } /* Allocate a pseudo tty if appropriate. */ if (tty_flag) { debug("Requesting pty."); /* Start the packet. */ packet_start(SSH_CMSG_REQUEST_PTY); /* Store TERM in the packet. There is no limit on the length of the string. */ cp = getenv("TERM"); if (!cp) cp = ""; packet_put_cstring(cp); /* Store window size in the packet. */ if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) memset(&ws, 0, sizeof(ws)); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); /* Store tty modes in the packet. */ tty_make_modes(fileno(stdin), NULL); /* Send the packet, and wait for it to leave. */ packet_send(); packet_write_wait(); /* Read response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { interactive = 1; have_tty = 1; } else if (type == SSH_SMSG_FAILURE) logit("Warning: Remote host failed or refused to " "allocate a pseudo tty."); else packet_disconnect("Protocol error waiting for pty " "request response."); } /* Request X11 forwarding if enabled and DISPLAY is set. */ display = getenv("DISPLAY"); if (options.forward_x11 && display != NULL) { char *proto, *data; /* Get reasonable local authentication information. */ client_x11_get_proto(display, options.xauth_location, options.forward_x11_trusted, options.forward_x11_timeout, &proto, &data); /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); x11_request_forwarding_with_spoofing(0, display, proto, data); /* Read response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { interactive = 1; } else if (type == SSH_SMSG_FAILURE) { logit("Warning: Remote host denied X11 forwarding."); } else { packet_disconnect("Protocol error waiting for X11 " "forwarding"); } } /* Tell the packet module whether this is an interactive session. */ packet_set_interactive(interactive, options.ip_qos_interactive, options.ip_qos_bulk); /* Request authentication agent forwarding if appropriate. */ check_agent_present(); if (options.forward_agent) { debug("Requesting authentication agent forwarding."); auth_request_forwarding(); /* Read response from the server. */ type = packet_read(); packet_check_eom(); if (type != SSH_SMSG_SUCCESS) logit("Warning: Remote host denied authentication agent forwarding."); } /* Initiate port forwardings. */ ssh_init_forwarding(); /* Execute a local command */ if (options.local_command != NULL && options.permit_local_command) ssh_local_cmd(options.local_command); /* * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ if (fork_after_authentication_flag) { if (options.exit_on_forward_failure && options.num_remote_forwards > 0) { debug("deferring postauth fork until remote forward " "confirmation received"); } else fork_postauth(); } /* * If a command was specified on the command line, execute the * command now. Otherwise request the server to start a shell. */ if (buffer_len(&command) > 0) { int len = buffer_len(&command); if (len > 900) len = 900; debug("Sending command: %.*s", len, (u_char *)buffer_ptr(&command)); packet_start(SSH_CMSG_EXEC_CMD); packet_put_string(buffer_ptr(&command), buffer_len(&command)); packet_send(); packet_write_wait(); } else { debug("Requesting shell."); packet_start(SSH_CMSG_EXEC_SHELL); packet_send(); packet_write_wait(); } /* Enter the interactive session. */ return client_loop(have_tty, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, 0); }
void kexgss_client(Kex *kex) { gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; Gssctxt *ctxt; OM_uint32 maj_status, min_status, ret_flags; u_int klen, kout, slen = 0, hashlen, strlen; DH *dh; BIGNUM *dh_server_pub = NULL; BIGNUM *shared_secret = NULL; BIGNUM *p = NULL; BIGNUM *g = NULL; u_char *kbuf, *hash; u_char *serverhostkey = NULL; u_char *empty = ""; char *msg; char *lang; int type = 0; int first = 1; int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; /* Initialise our GSSAPI world */ ssh_gssapi_build_ctx(&ctxt); if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) == GSS_C_NO_OID) fatal("Couldn't identify host exchange"); if (ssh_gssapi_import_name(ctxt, kex->gss_host)) fatal("Couldn't import hostname"); if (kex->gss_client && ssh_gssapi_client_identity(ctxt, kex->gss_client)) fatal("Couldn't acquire client credentials"); switch (kex->kex_type) { case KEX_GSS_GRP1_SHA1: dh = dh_new_group1(); break; case KEX_GSS_GRP14_SHA1: dh = dh_new_group14(); break; case KEX_GSS_GEX_SHA1: debug("Doing group exchange\n"); nbits = dh_estimate(kex->we_need * 8); packet_start(SSH2_MSG_KEXGSS_GROUPREQ); packet_put_int(min); packet_put_int(nbits); packet_put_int(max); packet_send(); packet_read_expect(SSH2_MSG_KEXGSS_GROUP); if ((p = BN_new()) == NULL) fatal("BN_new() failed"); packet_get_bignum2(p); if ((g = BN_new()) == NULL) fatal("BN_new() failed"); packet_get_bignum2(g); packet_check_eom(); if (BN_num_bits(p) < min || BN_num_bits(p) > max) fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", min, BN_num_bits(p), max); dh = dh_new_group(g, p); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } /* Step 1 - e is dh->pub_key */ dh_gen_key(dh, kex->we_need * 8); /* This is f, we initialise it now to make life easier */ dh_server_pub = BN_new(); if (dh_server_pub == NULL) fatal("dh_server_pub == NULL"); token_ptr = GSS_C_NO_BUFFER; do { debug("Calling gss_init_sec_context"); maj_status = ssh_gssapi_init_ctx(ctxt, kex->gss_deleg_creds, token_ptr, &send_tok, &ret_flags); if (GSS_ERROR(maj_status)) { if (send_tok.length != 0) { packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, send_tok.length); } fatal("gss_init_context failed"); } /* If we've got an old receive buffer get rid of it */ if (token_ptr != GSS_C_NO_BUFFER) xfree(recv_tok.value); if (maj_status == GSS_S_COMPLETE) { /* If mutual state flag is not true, kex fails */ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) fatal("Mutual authentication failed"); /* If integ avail flag is not true kex fails */ if (!(ret_flags & GSS_C_INTEG_FLAG)) fatal("Integrity check failed"); } /* * If we have data to send, then the last message that we * received cannot have been a 'complete'. */ if (send_tok.length != 0) { if (first) { packet_start(SSH2_MSG_KEXGSS_INIT); packet_put_string(send_tok.value, send_tok.length); packet_put_bignum2(dh->pub_key); first = 0; } else { packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, send_tok.length); } packet_send(); gss_release_buffer(&min_status, &send_tok); /* If we've sent them data, they should reply */ do { type = packet_read(); if (type == SSH2_MSG_KEXGSS_HOSTKEY) { debug("Received KEXGSS_HOSTKEY"); if (serverhostkey) fatal("Server host key received more than once"); serverhostkey = packet_get_string(&slen); } } while (type == SSH2_MSG_KEXGSS_HOSTKEY); switch (type) { case SSH2_MSG_KEXGSS_CONTINUE: debug("Received GSSAPI_CONTINUE"); if (maj_status == GSS_S_COMPLETE) fatal("GSSAPI Continue received from server when complete"); recv_tok.value = packet_get_string(&strlen); recv_tok.length = strlen; break; case SSH2_MSG_KEXGSS_COMPLETE: debug("Received GSSAPI_COMPLETE"); packet_get_bignum2(dh_server_pub); msg_tok.value = packet_get_string(&strlen); msg_tok.length = strlen; /* Is there a token included? */ if (packet_get_char()) { recv_tok.value= packet_get_string(&strlen); recv_tok.length = strlen; /* If we're already complete - protocol error */ if (maj_status == GSS_S_COMPLETE) packet_disconnect("Protocol error: received token when complete"); } else { /* No token included */ if (maj_status != GSS_S_COMPLETE) packet_disconnect("Protocol error: did not receive final token"); } break; case SSH2_MSG_KEXGSS_ERROR: debug("Received Error"); maj_status = packet_get_int(); min_status = packet_get_int(); msg = packet_get_string(NULL); lang = packet_get_string(NULL); fatal("GSSAPI Error: \n%.400s",msg); default: packet_disconnect("Protocol error: didn't expect packet type %d", type); } token_ptr = &recv_tok; } else { /* No data, and not complete */ if (maj_status != GSS_S_COMPLETE) fatal("Not complete, and no token output"); } } while (maj_status & GSS_S_CONTINUE_NEEDED); /* * We _must_ have received a COMPLETE message in reply from the * server, which will have set dh_server_pub and msg_tok */ if (type != SSH2_MSG_KEXGSS_COMPLETE) fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); /* Check f in range [1, p-1] */ if (!dh_pub_is_valid(dh, dh_server_pub)) packet_disconnect("bad server public DH value"); /* compute K=f^x mod p */ klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_server_pub, dh); if (kout < 0) fatal("DH_compute_key: failed"); shared_secret = BN_new(); if (shared_secret == NULL) fatal("kexgss_client: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexdh_client: BN_bin2bn failed"); memset(kbuf, 0, klen); xfree(kbuf); switch (kex->kex_type) { case KEX_GSS_GRP1_SHA1: case KEX_GSS_GRP14_SHA1: kex_dh_hash( kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->my), buffer_len(&kex->my), buffer_ptr(&kex->peer), buffer_len(&kex->peer), (serverhostkey ? serverhostkey : empty), slen, dh->pub_key, /* e */ dh_server_pub, /* f */ shared_secret, /* K */ &hash, &hashlen ); break; case KEX_GSS_GEX_SHA1: kexgex_hash( kex->evp_md, kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->my), buffer_len(&kex->my), buffer_ptr(&kex->peer), buffer_len(&kex->peer), (serverhostkey ? serverhostkey : empty), slen, min, nbits, max, dh->p, dh->g, dh->pub_key, dh_server_pub, shared_secret, &hash, &hashlen ); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } gssbuf.value = hash; gssbuf.length = hashlen; /* Verify that the hash matches the MIC we just got. */ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) packet_disconnect("Hash's MIC didn't verify"); xfree(msg_tok.value); DH_free(dh); if (serverhostkey) xfree(serverhostkey); BN_clear_free(dh_server_pub); /* save session id */ if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } if (kex->gss_deleg_creds) ssh_gssapi_credentials_updated(ctxt); if (gss_kex_context == NULL) gss_kex_context = ctxt; else ssh_gssapi_delete_ctx(&ctxt); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); }
void kexgss_server(Kex *kex) { OM_uint32 maj_status, min_status; /* * Some GSSAPI implementations use the input value of ret_flags (an * output variable) as a means of triggering mechanism specific * features. Initializing it to zero avoids inadvertently * activating this non-standard behaviour. */ OM_uint32 ret_flags = 0; gss_buffer_desc gssbuf, recv_tok, msg_tok; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; Gssctxt *ctxt = NULL; u_int slen, klen, kout, hashlen; u_char *kbuf, *hash; DH *dh; int min = -1, max = -1, nbits = -1; BIGNUM *shared_secret = NULL; BIGNUM *dh_client_pub = NULL; int type = 0; gss_OID oid; char *mechs; /* Initialise GSSAPI */ /* If we're rekeying, privsep means that some of the private structures * in the GSSAPI code are no longer available. This kludges them back * into life */ if (!ssh_gssapi_oid_table_ok()) if ((mechs = ssh_gssapi_server_mechanisms())) xfree(mechs); debug2("%s: Identifying %s", __func__, kex->name); oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); if (oid == GSS_C_NO_OID) fatal("Unknown gssapi mechanism"); debug2("%s: Acquiring credentials", __func__); if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) fatal("Unable to acquire credentials for the server"); switch (kex->kex_type) { case KEX_GSS_GRP1_SHA1: dh = dh_new_group1(); break; case KEX_GSS_GRP14_SHA1: dh = dh_new_group14(); break; case KEX_GSS_GEX_SHA1: debug("Doing group exchange"); packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); min = packet_get_int(); nbits = packet_get_int(); max = packet_get_int(); min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); packet_check_eom(); if (max < min || nbits < min || max < nbits) fatal("GSS_GEX, bad parameters: %d !< %d !< %d", min, nbits, max); dh = PRIVSEP(choose_dh(min, nbits, max)); if (dh == NULL) packet_disconnect("Protocol error: no matching group found"); packet_start(SSH2_MSG_KEXGSS_GROUP); packet_put_bignum2(dh->p); packet_put_bignum2(dh->g); packet_send(); packet_write_wait(); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } dh_gen_key(dh, kex->we_need * 8); do { debug("Wait SSH2_MSG_GSSAPI_INIT"); type = packet_read(); switch(type) { case SSH2_MSG_KEXGSS_INIT: if (dh_client_pub != NULL) fatal("Received KEXGSS_INIT after initialising"); recv_tok.value = packet_get_string(&slen); recv_tok.length = slen; if ((dh_client_pub = BN_new()) == NULL) fatal("dh_client_pub == NULL"); packet_get_bignum2(dh_client_pub); /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ break; case SSH2_MSG_KEXGSS_CONTINUE: recv_tok.value = packet_get_string(&slen); recv_tok.length = slen; break; default: packet_disconnect( "Protocol error: didn't expect packet type %d", type); } maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, &send_tok, &ret_flags)); xfree(recv_tok.value); if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) fatal("Zero length token output when incomplete"); if (dh_client_pub == NULL) fatal("No client public key"); if (maj_status & GSS_S_CONTINUE_NEEDED) { debug("Sending GSSAPI_CONTINUE"); packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, send_tok.length); packet_send(); gss_release_buffer(&min_status, &send_tok); } } while (maj_status & GSS_S_CONTINUE_NEEDED); if (GSS_ERROR(maj_status)) { if (send_tok.length > 0) { packet_start(SSH2_MSG_KEXGSS_CONTINUE); packet_put_string(send_tok.value, send_tok.length); packet_send(); } fatal("accept_ctx died"); } if (!(ret_flags & GSS_C_MUTUAL_FLAG)) fatal("Mutual Authentication flag wasn't set"); if (!(ret_flags & GSS_C_INTEG_FLAG)) fatal("Integrity flag wasn't set"); if (!dh_pub_is_valid(dh, dh_client_pub)) packet_disconnect("bad client public DH value"); klen = DH_size(dh); kbuf = xmalloc(klen); kout = DH_compute_key(kbuf, dh_client_pub, dh); if (kout < 0) fatal("DH_compute_key: failed"); shared_secret = BN_new(); if (shared_secret == NULL) fatal("kexgss_server: BN_new failed"); if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) fatal("kexgss_server: BN_bin2bn failed"); memset(kbuf, 0, klen); xfree(kbuf); switch (kex->kex_type) { case KEX_GSS_GRP1_SHA1: case KEX_GSS_GRP14_SHA1: kex_dh_hash( kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->peer), buffer_len(&kex->peer), buffer_ptr(&kex->my), buffer_len(&kex->my), NULL, 0, /* Change this if we start sending host keys */ dh_client_pub, dh->pub_key, shared_secret, &hash, &hashlen ); break; case KEX_GSS_GEX_SHA1: kexgex_hash( kex->evp_md, kex->client_version_string, kex->server_version_string, buffer_ptr(&kex->peer), buffer_len(&kex->peer), buffer_ptr(&kex->my), buffer_len(&kex->my), NULL, 0, min, nbits, max, dh->p, dh->g, dh_client_pub, dh->pub_key, shared_secret, &hash, &hashlen ); break; default: fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); } BN_clear_free(dh_client_pub); if (kex->session_id == NULL) { kex->session_id_len = hashlen; kex->session_id = xmalloc(kex->session_id_len); memcpy(kex->session_id, hash, kex->session_id_len); } gssbuf.value = hash; gssbuf.length = hashlen; if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) fatal("Couldn't get MIC"); packet_start(SSH2_MSG_KEXGSS_COMPLETE); packet_put_bignum2(dh->pub_key); packet_put_string(msg_tok.value,msg_tok.length); if (send_tok.length != 0) { packet_put_char(1); /* true */ packet_put_string(send_tok.value, send_tok.length); } else { packet_put_char(0); /* false */ } packet_send(); gss_release_buffer(&min_status, &send_tok); gss_release_buffer(&min_status, &msg_tok); if (gss_kex_context == NULL) gss_kex_context = ctxt; else ssh_gssapi_delete_ctx(&ctxt); DH_free(dh); kex_derive_keys(kex, hash, hashlen, shared_secret); BN_clear_free(shared_secret); kex_finish(kex); /* If this was a rekey, then save out any delegated credentials we * just exchanged. */ if (options.gss_store_rekey) ssh_gssapi_rekey_creds(); }
apr_status_t port_socket_set_option(port_t *self, term_t opt, term_t value) { port_socket_data_t *data = self->data; if (opt == A_EXPECT) { if (!is_int(value)) return APR_BADARG; data->expected_size = int_value2(value); if (data->expected_size < 0) return APR_BADARG; if (!is_pid(self->owner_in)) return APR_ENOPROC; //enough data may already be there if (data->expected_size == 0 && buffer_len(data->in_buf) > 0 || data->expected_size > 0 && buffer_len(data->in_buf) >= data->expected_size) { int len = (data->expected_size == 0) ?buffer_len(data->in_buf) :data->expected_size; xpool_t *tmp = xpool_make(self->pool); term_t bin = make_binary(intnum(len), buffer_ptr(data->in_buf), tmp); term_t msg = make_tuple3(A_TCP, port_id(self, tmp), bin, tmp); process_t *proc = proc_lookup(pid_serial(self->owner_in)); proc_new_mail(proc, msg); buffer_consume(data->in_buf, len); xpool_destroy(tmp); } else data->packet_expected = 1; } else if (opt == A_REQUIRE) { int size; if (!is_int(value)) return APR_BADARG; size = int_value2(value); if (size < 0 || size > SOCK_OUTBUF_LEN) return APR_BADARG; data->required_size = size; if (buffer_available(data->out_buf) >= size) { xpool_t *tmp = xpool_make(self->pool); int avail = buffer_available(data->out_buf); term_t msg = make_tuple3(A_TCP_SPACE, port_id(self, tmp), intnum(avail), tmp); process_t *proc = proc_lookup(pid_serial(self->owner_out)); //TODO: insure that only owner can send to socket proc_new_mail(proc, msg); xpool_destroy(tmp); data->space_required = 0; } else data->space_required = 1; } else return APR_BADARG; return APR_SUCCESS; }