/* * smbrdr_hdr_process * * Assuming 'srh->srh_mbuf' contains a response from a Windows client, * decodes the 32 bytes SMB header. * * Buffer overflow typically means that the server has more data than * it could fit in the response buffer. The client can use subsequent * SmbReadX requests to obtain the remaining data (KB 193839). * * Returns: * * NT_STATUS_INVALID_NETWORK_RESPONSE error decoding the header * NT_STATUS_REPLY_MESSAGE_MISMATCH response doesn't match the request * NT_STATUS_SUCCESS successful * smb_hdr->status.ntstatus error returned by server */ static DWORD smbrdr_hdr_process(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr) { int rc; rc = smb_decode_nt_hdr(&srh->srh_mbuf, smb_hdr); if (rc < SMB_HEADER_LEN) { smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_hdr_process[%d]: invalid header (%d)", srh->srh_cmd, rc); return (NT_STATUS_INVALID_NETWORK_RESPONSE); } switch (NT_SC_VALUE(smb_hdr->status.ntstatus)) { case NT_STATUS_SUCCESS: case NT_STATUS_BUFFER_OVERFLOW: break; default: smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_hdr_process[%d]: request failed (%s)", srh->srh_cmd, xlate_nt_status(smb_hdr->status.ntstatus)); return (smb_hdr->status.ntstatus); } if (smb_hdr->command != srh->srh_cmd) { smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_hdr_process[%d]: reply mismatch (%d)", srh->srh_cmd, smb_hdr->command); return (NT_STATUS_REPLY_MESSAGE_MISMATCH); } return (NT_STATUS_SUCCESS); }
/* * smbrdr_send * * Send the SMB packet pointed by the given handle over * network. * * Returns: * * NT_STATUS_INTERNAL_ERROR crypto framework failure * NT_STATUS_UNEXPECTED_NETWORK_ERROR send failed * NT_STATUS_SUCCESS successful */ DWORD smbrdr_send(smbrdr_handle_t *srh) { int rc; if (smbrdr_sign(&srh->srh_session->sign_ctx, &srh->srh_mbuf) != SMBAUTH_SUCCESS) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_send[%d]: signing failed", srh->srh_cmd); return (NT_STATUS_INTERNAL_ERROR); } rc = nb_send(srh->srh_session->sock, srh->srh_buf, smb_msgbuf_used(&srh->srh_mbuf)); if (rc < 0) { /* * Make the sequence number of the next SMB request even * to avoid DC from failing the next SMB request with * ACCESS_DENIED. */ smb_mac_dec_seqnum(&srh->srh_session->sign_ctx); smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_send[%d]: send failed (%d)", srh->srh_cmd, rc); return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); } return (NT_STATUS_SUCCESS); }
/*ARGSUSED*/ static void * smbd_share_printers(void *arg) { cups_dest_t *dests; cups_dest_t *dest; smb_cups_ops_t *cups; smb_share_t si; uint32_t nerr; int num_dests; int i; if (!smb_config_getbool(SMB_CI_PRINT_ENABLE)) return (NULL); if ((cups = smbd_cups_ops()) == NULL) return (NULL); if (smb_shr_get(SMB_SHARE_PRINT, &si) != NERR_Success) { smb_log(smbd.s_loghd, LOG_DEBUG, "smbd_share_printers unable to load %s", SMB_SHARE_PRINT); return (NULL); } num_dests = cups->cupsGetDests(&dests); for (i = num_dests, dest = dests; i > 0; i--, dest++) { if (dest->instance != NULL) continue; (void) strlcpy(si.shr_name, dest->name, MAXPATHLEN); smbd_print_share_comment(&si, dest); si.shr_type = STYPE_PRINTQ; nerr = smb_shr_add(&si); if (nerr == NERR_Success || nerr == NERR_DuplicateShare) smb_log(smbd.s_loghd, LOG_DEBUG, "shared printer: %s", si.shr_name); else smb_log(smbd.s_loghd, LOG_DEBUG, "smbd_share_printers: unable to add share %s: %u", si.shr_name, nerr); } cups->cupsFreeDests(num_dests, dests); return (NULL); }
/* * Wait until the service is online. Provided for threads that * should wait until the service has been fully initialized before * they start performing operations. */ void smbd_online_wait(const char *text) { while (!smbd_online()) (void) sleep(SMBD_ONLINE_WAIT_INTERVAL); if (text != NULL) { smb_log(smbd.s_loghd, LOG_DEBUG, "%s: online", text); (void) fprintf(stderr, "%s: online\n", text); } }
/* * smbrdr_rcv * * Receive a SMB response and decode the packet header. * * "Implementing CIFS" book, SMB requests always have an even sequence * number and replies always have an odd. * * With the original code, if the SMB Redirector skip the counter increment * in the event of any failure during SmbSessionSetupAndX, it causes the * domain controller to fail the next SMB request(odd sequence number) * with ACCESS_DENIED. * * Smbrdr module should use the same sequence number (i.e. ssc_seqnum of the * SMB Sign context) for generating the MAC signature for all incoming * responses per SmbTransact request. Otherwise, the validation will fail. * It is now fixed by decrementing the sequence number prior to validating * the subsequent responses for a single request. * * Returns: * * status code returned by smbrdr_hdr_process() * NT_STATUS_UNEXPECTED_NETWORK_ERROR receive failed * NT_STATUS_SUCCESS successful */ DWORD smbrdr_rcv(smbrdr_handle_t *srh, int is_first_rsp) { smb_hdr_t smb_hdr; DWORD status; int rc; smb_sign_ctx_t *sign_ctx = &srh->srh_session->sign_ctx; rc = nb_rcv(srh->srh_session->sock, srh->srh_buf, SMBRDR_REQ_BUFSZ, 0); if (rc < 0) { smb_mac_inc_seqnum(sign_ctx); smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_rcv[%d]: receive failed (%d)", srh->srh_cmd, rc); return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); } smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, rc, srh->srh_mbflags); status = smbrdr_hdr_process(srh, &smb_hdr); if (status != NT_STATUS_SUCCESS) { smb_mac_inc_seqnum(sign_ctx); smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_rcv[%d]: failed (%s)", srh->srh_cmd, xlate_nt_status(status)); return (status); } if (!is_first_rsp) smb_mac_dec_seqnum(sign_ctx); if (!smbrdr_sign_chk(sign_ctx, &srh->srh_mbuf, smb_hdr.extra.extra.security_sig)) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_rcv[%d]: bad signature", srh->srh_cmd); return (NT_STATUS_INVALID_NETWORK_RESPONSE); } return (NT_STATUS_SUCCESS); }
/* * smbrdr_sign * * Signs the given outgoing packet according to the * specified signing context. * * The client and server each maintain an integer counter * which they initialize to zero. Both counters are * incremented for every SMB message - that's once for a * request and once for a reply. As a result, requests sent * by SMB Redirector always have an even sequence number * and replies from the Windows server always have an odd * number. * * Based on the observed Windows 2003 behavior, any SMB * request will fail with NT_STATUS_ACCESS_DENIED if its * sequence number is not even. * * The function can fail if there is trouble with the cryptographic * framework and if that happens SMBAUTH_FAILURE is returned. In the * normal case SMBAUTH_SUCCESS is returned. */ static int smbrdr_sign(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb) { if (sign_ctx->ssc_flags & SMB_SCF_STARTED) { if (sign_ctx->ssc_seqnum % 2) { smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_sign: invalid sequence (%d)", sign_ctx->ssc_seqnum); } if (smb_mac_sign(sign_ctx, smb_msgbuf_base(mb), smb_msgbuf_used(mb)) != SMBAUTH_SUCCESS) return (SMBAUTH_FAILURE); sign_ctx->ssc_seqnum++; } return (SMBAUTH_SUCCESS); }
/* * Start the spool thread. * Returns 0 on success, an error number if thread creation fails. */ void smbd_spool_start(void) { pthread_attr_t attr; int rc; if (!smb_config_getbool(SMB_CI_PRINT_ENABLE)) return; (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&smbd.s_spool_tid, &attr, smbd_spool_monitor, NULL); (void) pthread_attr_destroy(&attr); if (rc != 0) smb_log(smbd.s_loghd, LOG_NOTICE, "failed to start print monitor: %s", strerror(errno)); }
void smbd_load_printers(void) { pthread_t tid; pthread_attr_t attr; int rc; if (!smb_config_getbool(SMB_CI_PRINT_ENABLE)) return; (void) pthread_attr_init(&attr); (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&tid, &attr, smbd_share_printers, &tid); (void) pthread_attr_destroy(&attr); if (rc != 0) smb_log(smbd.s_loghd, LOG_NOTICE, "unable to load printer shares: %s", strerror(errno)); }
/* * smbrdr_transact * * Send a SMB_COM_TRANSACTION request. */ int smbrdr_transact(int fid, char *out_buf, int out_len, char *in_buf, int in_len) { struct sdb_session *session; struct sdb_netuse *netuse; struct sdb_ofile *ofile; struct sdb_logon *logon; smb_transact_rsp_t rsp; smbrdr_handle_t srh; smb_msgbuf_t *mb; DWORD status; int rc; unsigned short rcv_dcnt; int cur_inlen; int first_rsp; if ((ofile = smbrdr_ofile_get(fid)) == 0) return (-1); netuse = ofile->netuse; session = netuse->session; logon = &session->logon; status = smbrdr_request_init(&srh, SMB_COM_TRANSACTION, session, logon, netuse); if (status != NT_STATUS_SUCCESS) { smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_transact: %s", xlate_nt_status(status)); smbrdr_ofile_put(ofile); return (-1); } mb = &srh.srh_mbuf; rc = prep_smb_transact(mb, ofile->fid, out_buf, out_len, in_len, session->remote_caps & CAP_UNICODE); if (rc < 0) { smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_transact: prep failed"); smbrdr_handle_free(&srh); smbrdr_ofile_put(ofile); return (rc); } smbrdr_lock_transport(); status = smbrdr_send(&srh); if (status != NT_STATUS_SUCCESS) { smbrdr_unlock_transport(); smbrdr_handle_free(&srh); smbrdr_ofile_put(ofile); smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_transact: send failed"); return (-1); } rcv_dcnt = 0; cur_inlen = in_len; first_rsp = 1; do { if (smbrdr_rcv(&srh, first_rsp) != NT_STATUS_SUCCESS) { smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_transact: nb_rcv failed"); rc = -1; break; } rc = decode_smb_transact(mb, in_buf, cur_inlen, &rsp); if (rc < 0 || rsp.TotalDataCount > in_len) { smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_transact: decode failed"); rc = -1; break; } rcv_dcnt += rsp.DataCount; cur_inlen -= rsp.DataCount; first_rsp = 0; } while (rcv_dcnt < rsp.TotalDataCount); smbrdr_unlock_transport(); smbrdr_handle_free(&srh); smbrdr_ofile_put(ofile); return ((rc < 0) ? rc : rcv_dcnt); }
int smbd_cups_init(void) { (void) mutex_lock(&smbd_cups_mutex); if (smb_cups.cups_hdl != NULL) { (void) mutex_unlock(&smbd_cups_mutex); return (0); } if ((smb_cups.cups_hdl = dlopen("libcups.so.2", RTLD_NOW)) == NULL) { (void) mutex_unlock(&smbd_cups_mutex); smb_log(smbd.s_loghd, LOG_DEBUG, "smbd_cups_init: cannot open libcups"); return (ENOENT); } smb_cups.cupsLangDefault = (cups_lang_t *(*)())dlsym(smb_cups.cups_hdl, "cupsLangDefault"); smb_cups.cupsLangEncoding = (const char *(*)(cups_lang_t *)) dlsym(smb_cups.cups_hdl, "cupsLangEncoding"); smb_cups.cupsDoFileRequest = (ipp_t *(*)(http_t *, ipp_t *, const char *, const char *)) dlsym(smb_cups.cups_hdl, "cupsDoFileRequest"); smb_cups.cupsLastError = (ipp_status_t (*)()) dlsym(smb_cups.cups_hdl, "cupsLastError"); smb_cups.cupsLangFree = (void (*)(cups_lang_t *)) dlsym(smb_cups.cups_hdl, "cupsLangFree"); smb_cups.cupsGetDests = (int (*)(cups_dest_t **)) dlsym(smb_cups.cups_hdl, "cupsGetDests"); smb_cups.cupsFreeDests = (void (*)(int, cups_dest_t *)) dlsym(smb_cups.cups_hdl, "cupsFreeDests"); smb_cups.httpClose = (void (*)(http_t *)) dlsym(smb_cups.cups_hdl, "httpClose"); smb_cups.httpConnect = (http_t *(*)(const char *, int)) dlsym(smb_cups.cups_hdl, "httpConnect"); smb_cups.ippNew = (ipp_t *(*)())dlsym(smb_cups.cups_hdl, "ippNew"); smb_cups.ippDelete = (void (*)())dlsym(smb_cups.cups_hdl, "ippDelete"); smb_cups.ippErrorString = (char *(*)()) dlsym(smb_cups.cups_hdl, "ippErrorString"); smb_cups.ippAddString = (ipp_attribute_t *(*)()) dlsym(smb_cups.cups_hdl, "ippAddString"); if (smb_cups.cupsLangDefault == NULL || smb_cups.cupsLangEncoding == NULL || smb_cups.cupsDoFileRequest == NULL || smb_cups.cupsLastError == NULL || smb_cups.cupsLangFree == NULL || smb_cups.cupsGetDests == NULL || smb_cups.cupsFreeDests == NULL || smb_cups.ippNew == NULL || smb_cups.httpClose == NULL || smb_cups.httpConnect == NULL || smb_cups.ippDelete == NULL || smb_cups.ippErrorString == NULL || smb_cups.ippAddString == NULL) { (void) dlclose(smb_cups.cups_hdl); smb_cups.cups_hdl = NULL; (void) mutex_unlock(&smbd_cups_mutex); smb_log(smbd.s_loghd, LOG_DEBUG, "smbd_cups_init: cannot load libcups"); return (ENOENT); } (void) mutex_unlock(&smbd_cups_mutex); return (0); }
/* * All versions of windows use this function to spool files to a printer * via the cups interface */ static void smbd_spool_copyfile(smb_inaddr_t *ipaddr, char *username, char *path, char *doc_name) { smb_cups_ops_t *cups; http_t *http = NULL; /* HTTP connection to server */ ipp_t *request = NULL; /* IPP Request */ ipp_t *response = NULL; /* IPP Response */ cups_lang_t *language = NULL; /* Default language */ char uri[HTTP_MAX_URI]; /* printer-uri attribute */ char new_jobname[SMBD_PJOBLEN]; smbd_printjob_t pjob; char clientname[INET6_ADDRSTRLEN]; struct stat sbuf; int rc = 1; if (stat(path, &sbuf)) { smb_log(smbd.s_loghd, LOG_INFO, "smbd_spool_copyfile: %s: %s", path, strerror(errno)); return; } /* * Remove zero size files and return; these were inadvertantly * created by XP or 2000. */ if (sbuf.st_size == 0) { if (remove(path) != 0) smb_log(smbd.s_loghd, LOG_INFO, "smbd_spool_copyfile: cannot remove %s: %s", path, strerror(errno)); return; } if ((cups = smbd_cups_ops()) == NULL) return; if ((http = cups->httpConnect("localhost", 631)) == NULL) { smb_log(smbd.s_loghd, LOG_INFO, "smbd_spool_copyfile: cupsd not running"); return; } if ((request = cups->ippNew()) == NULL) { smb_log(smbd.s_loghd, LOG_INFO, "smbd_spool_copyfile: ipp not running"); return; } request->request.op.operation_id = IPP_PRINT_JOB; request->request.op.request_id = 1; language = cups->cupsLangDefault(); cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cups->cupsLangEncoding(language)); cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language->language); (void) snprintf(uri, sizeof (uri), "ipp://localhost/printers/%s", SMBD_PRINTER); pjob.pj_pid = pthread_self(); pjob.pj_sysjob = 10; (void) strlcpy(pjob.pj_filename, path, SMBD_PJOBLEN); pjob.pj_start_time = time(NULL); pjob.pj_status = 2; pjob.pj_size = sbuf.st_blocks * 512; pjob.pj_page_count = 1; pjob.pj_isspooled = B_TRUE; pjob.pj_jobnum = smbd_cups_jobnum; (void) strlcpy(pjob.pj_jobname, doc_name, SMBD_PJOBLEN); (void) strlcpy(pjob.pj_username, username, SMBD_PJOBLEN); (void) strlcpy(pjob.pj_queuename, SMBD_CUPS_SPOOL_DIR, SMBD_PJOBLEN); cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, pjob.pj_username); if (smb_inet_ntop(ipaddr, clientname, SMB_IPSTRLEN(ipaddr->a_family)) == NULL) { smb_log(smbd.s_loghd, LOG_INFO, "smbd_spool_copyfile: %s: unknown client", clientname); goto out; } cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-originating-host-name", NULL, clientname); (void) snprintf(new_jobname, SMBD_PJOBLEN, "%s%d", SMBD_FN_PREFIX, pjob.pj_jobnum); cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, new_jobname); (void) snprintf(uri, sizeof (uri) - 1, "/printers/%s", SMBD_PRINTER); response = cups->cupsDoFileRequest(http, request, uri, pjob.pj_filename); if (response != NULL) { if (response->request.status.status_code >= IPP_OK_CONFLICT) { smb_log(smbd.s_loghd, LOG_ERR, "smbd_spool_copyfile: printer %s: %s", SMBD_PRINTER, cups->ippErrorString(cups->cupsLastError())); } else { atomic_inc_32(&smbd_cups_jobnum); rc = 0; } } else { smb_log(smbd.s_loghd, LOG_ERR, "smbd_spool_copyfile: unable to print to %s", cups->ippErrorString(cups->cupsLastError())); } if (rc == 0) (void) unlink(pjob.pj_filename); out: if (response) cups->ippDelete(response); if (language) cups->cupsLangFree(language); if (http) cups->httpClose(http); }
/* * smbrdr_exchange * * Send the SMB packet pointed by the given handle over * network. Receive the response and decode the packet header. * * From "Implementing CIFS" book, SMB requests always have an even sequence * number and replies always have an odd. * * With the original code, if the SMB Redirector skips the counter increment * in the event of any failure during SmbSessionSetupAndX, it causes the * domain controller to fail the next SMB request(odd sequence number) * with ACCESS_DENIED. * * Returns: * * status code returned by smbrdr_hdr_process() * NT_STATUS_INTERNAL_ERROR crypto framework failure * NT_STATUS_UNEXPECTED_NETWORK_ERROR send/receive failed * NT_STATUS_SUCCESS successful */ DWORD smbrdr_exchange(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr, long timeout) { smb_sign_ctx_t *sign_ctx; smb_msgbuf_t *mb; DWORD status; int rc; smbrdr_lock_transport(); mb = &srh->srh_mbuf; sign_ctx = &srh->srh_session->sign_ctx; if (smbrdr_sign(sign_ctx, mb) != SMBAUTH_SUCCESS) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_exchange[%d]: signing failed", srh->srh_cmd); smbrdr_unlock_transport(); return (NT_STATUS_INTERNAL_ERROR); } rc = nb_exchange(srh->srh_session->sock, srh->srh_buf, smb_msgbuf_used(mb), srh->srh_buf, SMBRDR_REQ_BUFSZ, timeout); if (rc < 0) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_exchange[%d]: failed (%d)", srh->srh_cmd, rc); if (srh->srh_cmd != SMB_COM_ECHO) { /* * Since SMB echo is used to check the session * status then don't destroy the session if it's * SMB echo. */ srh->srh_session->state = SDB_SSTATE_STALE; } smb_mac_inc_seqnum(sign_ctx); smbrdr_unlock_transport(); return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); } /* initialize for processing response */ smb_msgbuf_init(mb, srh->srh_buf, rc, srh->srh_mbflags); status = smbrdr_hdr_process(srh, smb_hdr); if (status != NT_STATUS_SUCCESS) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_exchange[%d]: failed (%s)", srh->srh_cmd, xlate_nt_status(status)); smb_mac_inc_seqnum(sign_ctx); smbrdr_unlock_transport(); return (status); } /* Signature validation */ if (!smbrdr_sign_chk(sign_ctx, mb, smb_hdr->extra.extra.security_sig)) { smb_log(smbrdr_log_hdl, LOG_ERR, "smbrdr_exchange[%d]: bad signature", srh->srh_cmd); smbrdr_unlock_transport(); return (NT_STATUS_INVALID_NETWORK_RESPONSE); } smbrdr_unlock_transport(); return (NT_STATUS_SUCCESS); }